Std :: list and std :: for_each: where is my end?

Consider the following minimal example:

#include <functional> #include <algorithm> #include <list> int main() { std::list<std::function<void()>> list; list.push_back([&list](){ list.push_back([](){ throw; }); }); std::for_each(list.cbegin(), list.cend(), [](auto &&f) { f(); }); } 

It compiles and throws an exception at runtime.
I assumed that only the first lambda is executed by std::for_each , but apparently I was wrong: if I add another lambda at the end of the list, the iteration will also reach that lambda.

Let's return an example ( push_front instead of push_back and crbegin / crend instead of cbegin / cend ):

 #include <functional> #include <algorithm> #include <list> int main() { std::list<std::function<void()>> list; list.push_front([&list](){ list.push_front([](){ throw; }); }); std::for_each(list.crbegin(), list.crend(), [](auto &&f) { f(); }); } 

Due to the previous example, I expected this to compile and work too.
Instead, it compiles and does not crash. This time, the function shifted to the top of the list is not executed.

The question is pretty simple: is this correct?
Why are two examples so conflicting?

In the first case, I expected something else, and I was mistaken, this is not a problem.
Anyway, I would expect consistency between the two cycles. I mean, the second function is executed in one case, and it is not executed in another case, but I iterate from beginning to end in both cases.
What is wrong in my reasoning?

+7
c ++ iterator foreach stdlist
source share
1 answer

Honestly, the results you get seem to be what I expected. Skip the first example:

one.

 list.push_back([&list](){ list.push_back([](){ throw; }); }); 

List Status:

 +-- list.begin() (not necessarily what has been passed to for_each) v [lambda]----[end] 

2. start iterating over the list

Iteration 1:

List Status:

 +-- list.begin() (not necessarily what has been passed to for_each) v [lambda]----[end] ^ +-- current 

f() calls list.push_back([](){ throw; });

List Status:

 +-- list.begin() (not necessarily what has been passed to for_each) v [lambda]----[inner_lambda]----[end] ^ +-- current 

Iteration 2: ( ++current )

List Status:

 +-- list.begin() (not necessarily what has been passed to for_each) v [lambda]----[inner_lambda]----[end] ^ +-- current 

f() calls throw;

end



Now let's do another direction.

First of all, look at how reverse iterators are actually represented - this is important (image from cppreference): reverse iterators

The important part: return endpoints to a normal start. But the problem is that with the list you can insert something before begin , but this is not possible after end . This invariant splits into inverse iterators.

one.

 list.push_front([&list](){ list.push_front([](){ throw; }); }); 

List Status:

 +-- list.begin() (not necessarily what has been passed to for_each) | |+-- list.rend().base() || || +-- list.rbegin().base() vv v [lambda]----[end] 

2. start iterating over the list

Iteration 1:

List Status:

 +-- list.begin() (not necessarily what has been passed to for_each) | |+-- list.rend().base() || || +-- list.rbegin().base() vv v [lambda]----[end] ^ ^ | +---- current | +--------- passed list.rend() 

*current gives [lambda] .

f() calls list.push_front([](){ throw; });

List Status:

 +-- list.begin() (not necessarily what has been passed to for_each) | |+-- list.rend().base() || || +-- list.rbegin().base() vv v [inner_lambda]----[lambda]----[end] ^ ^ | +---- current | +--------- passed list.rend().base() 

Note that the past list.rend().base() not changed - but it no longer points to the first (last last) element.

Iteration 2: ( ++current )

 +-- list.begin() (not necessarily what has been passed to for_each) | |+-- list.rend().base() || || +-- list.rbegin().base() vv v [inner_lambda]----[lambda]----[end] ^ ^ | +---- current | +--------- passed list.rend().base() 

current == passed list.rend().base()

end



Now try another one by my mistake, this part is relevant for fast forward in the list :

one.

 list.push_front([&list](){ list.push_front([](){ throw; }); }); 

List Status:

 +-- list.begin() (not necessarily what has been passed to for_each) v [lambda]----[end] 

2. start iterating over the list

Iteration 1:

List Status:

 +-- list.begin() (not necessarily what has been passed to for_each) v [lambda]----[end] ^ +-- current 

f() calls list.push_front([](){ throw; });

The current iterator is not recognized as invalid and / or is not indicated elsewhere than it has already indicated.

List Status:

 +-- list.begin() (not necessarily what has been passed to for_each) v [inner_lambda]----[lambda]----[end] ^ +-- current 

Iteration 2: ( ++current )

List Status:

 +-- list.begin() (not necessarily what has been passed to for_each) v [inner_lambda]----[lambda]----[end] ^ +-- current 

end

+6
source share

All Articles