std :: list和std :: for_each:我的结局在哪里?

sky*_*ack 7 c++ foreach iterator stdlist

请考虑以下最小示例:

#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(); });
}
Run Code Online (Sandbox Code Playgroud)

它在运行时编译并抛出异常.
我的猜测是只有第一个lambda被执行std::for_each,但显然我错了:如果我在列表的末尾添加另一个lambda,迭代也会达到lambda.

让我们恢复示例(push_front而不是push_backcrbegin/ crend而不是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(); });
}
Run Code Online (Sandbox Code Playgroud)

由于前面的例子,我预计这也会编译和崩溃.
相反,它编译并且不会崩溃.这次,不执行推到列表前面的功能.

问题很简单:这是正确的吗?
为什么两个例子如此违反直觉?

在第一种情况下,我期待一些不同的东西,我错了,这不是问题.
无论如何,我希望两个循环之间的一致性.我的意思是,第二个函数在一个案例中执行,而在另一个案例中不执行,但我在两种情况下都是从开始结束迭代.
我的推理出了什么问题?

krz*_*zaq 6

说实话,你得到的结果似乎是我的预期.让我们先看一下你的例子:

1.

list.push_back([&list](){ list.push_back([](){ throw; }); });
Run Code Online (Sandbox Code Playgroud)

列表状态:

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

2.开始迭代列表

迭代1:

列表状态:

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

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

列表状态:

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

迭代2 :( ++current)

列表状态:

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

f() 电话 throw;

结束



现在让我们做另一个方向.

首先,看看反向迭代器实际上是如何表示的 - 这很重要(来自cppreference的图像): 反向迭代器

重要的部分是:反向终点指向正常开始.但问题是,通过列表,人们可以在之前插入一些东西begin,但之后不可能end.反向迭代器打破了这个不变量.

1.

list.push_front([&list](){ list.push_front([](){ throw; }); });
Run Code Online (Sandbox Code Playgroud)

列表状态:

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

2.开始迭代列表

迭代1:

列表状态:

+-- 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()
Run Code Online (Sandbox Code Playgroud)

*current收益率[lambda].

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

列表状态:

+-- 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()
Run Code Online (Sandbox Code Playgroud)

注意,传递list.rend().base()没有改变 - 但它不再指向第一个(过去最后一个反转)元素.

迭代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()
Run Code Online (Sandbox Code Playgroud)

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

结束



现在让我们尝试另一个错误,这部分与前导迭代列表相关:

1.

list.push_front([&list](){ list.push_front([](){ throw; }); });
Run Code Online (Sandbox Code Playgroud)

列表状态:

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

2.开始迭代列表

迭代1:

列表状态:

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

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

迭代器到当前不会失效和/或指向其他地方而不是指向它.

列表状态:

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

迭代2 :( ++current)

列表状态:

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

结束