std::list and std::for_each:我的尽头在哪里?
std::list and std::for_each: where is my end?
考虑以下最小示例:
#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(); });
}
它在 运行 时编译并抛出异常。
我的猜测是只有第一个 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(); });
}
由于前面的示例,我预计它也会编译并崩溃。
相反,它会编译并且不会崩溃。这次推到列表前面的函数没有执行。
问题很简单:这是正确的吗?
为什么这两个例子如此违反直觉?
在第一种情况下,我期待的是不同的东西,但我错了,这不是问题。
不管怎样,我希望这两个循环之间是一致的。我的意思是,第二个函数在一种情况下执行,而在另一种情况下不执行,但我从 begin 迭代到 end 在这两种情况下。
我的推理有什么问题?
说实话,你得到的结果似乎是我所期望的。让我们通过你的第一个例子:
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):
重要的部分是:反向结束指向正常开始。但问题是,对于列表,一个 可以 在 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; });
current 的迭代器没有失效and/or指向它已经指向的其他地方。
列表状态:
+-- 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
结束
考虑以下最小示例:
#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(); });
}
它在 运行 时编译并抛出异常。
我的猜测是只有第一个 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(); });
}
由于前面的示例,我预计它也会编译并崩溃。
相反,它会编译并且不会崩溃。这次推到列表前面的函数没有执行。
问题很简单:这是正确的吗?
为什么这两个例子如此违反直觉?
在第一种情况下,我期待的是不同的东西,但我错了,这不是问题。
不管怎样,我希望这两个循环之间是一致的。我的意思是,第二个函数在一种情况下执行,而在另一种情况下不执行,但我从 begin 迭代到 end 在这两种情况下。
我的推理有什么问题?
说实话,你得到的结果似乎是我所期望的。让我们通过你的第一个例子:
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):
重要的部分是:反向结束指向正常开始。但问题是,对于列表,一个 可以 在 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; });
current 的迭代器没有失效and/or指向它已经指向的其他地方。
列表状态:
+-- 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
结束