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_back
和crbegin
/ 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)
由于前面的例子,我预计这也会编译和崩溃.
相反,它编译并且不会崩溃.这次,不执行推到列表前面的功能.
问题很简单:这是正确的吗?
为什么两个例子如此违反直觉?
在第一种情况下,我期待一些不同的东西,我错了,这不是问题.
无论如何,我希望两个循环之间的一致性.我的意思是,第二个函数在一个案例中执行,而在另一个案例中不执行,但我在两种情况下都是从开始到结束迭代.
我的推理出了什么问题?
说实话,你得到的结果似乎是我的预期.让我们先看一下你的例子:
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)
结束