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

时间:2016-11-21 13:27:37

标签: 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(); });
}

它在运行时编译并抛出异常 我的猜测是std::for_each只执行了第一个lambda,但显然我错了:如果我在列表的末尾添加另一个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(); });
}

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

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

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

1 个答案:

答案 0 :(得分:6)

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

1

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

列出状态:

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

2。开始迭代列表

迭代1:

列出状态:

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

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

列出状态:

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

迭代2:(++current

列出状态:

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

f()来电throw;

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

首先,看一下反向迭代器的实际表示方式 - 这很重要(来自cppreference的图像):reverse iterators

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

1

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

列出状态:

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

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()

*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()

请注意,传递的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()

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

结束

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

1

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

列出状态:

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

2。开始迭代列表

迭代1:

列出状态:

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

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

当前的迭代器不会失效和/或指向其他位置,而不是指向它。

列出状态:

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

迭代2:(++current

列出状态:

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