标准是否明确禁止在std::for_each
内修改容器?
更具体地说,在std::list
的情况下,修改列表时迭代器不会失效。因此,以下代码有效:
std::list<int> list;
list.push_front(5);
list.push_front(10);
auto it = list.end();
it--; // point to 5
std::for_each(list.begin(), list.end(), [&](int i){
/* the line below will remove the last element in the list;
* list will have only one element (the currently processed one);
* list.end() is not invalidated and we exit for_each() */
list.erase(it);
});
这绝对是一个糟糕的代码。但这是合法的吗?
答案 0 :(得分:3)
标准是否明确禁止在
std::for_each
内修改容器?
我唯一能想到的就是这个代码不符合标准的是[alg.foreach]
我们有
复杂性:正好
f
次last - first
。
f
是函数for_each
适用。
由于修改了列表并删除了一个元素,我们不再满足这种复杂性。我不知道是否会使它不符合要求,但我唯一能看到的是,在使用for_each
答案 1 :(得分:3)
标准是否明确禁止修改其中的容器
std::for_each
?
不,不是明确。至少对于“明确”的任何合理定义。关于std::for_each
(第25.2.4节)的部分没有说明导致修改迭代范围的迭代器失效或副作用。
但是,有很多隐含原因导致您的代码无法正常工作。考虑一下std::for_each
必须如何实施。它完全是通用的,因此对它所运行的std::list
一无所知;你给了它两个迭代器来形成一个范围,这意味着可以从last
到达first
。 std::for_each
应该如何知道您的函数对象使用first
到last
的本地迭代器对象无效?
即使函数知道失效已经发生,它应该怎么办?在手写循环中,您将获取erase
的结果并继续使用它,但根据定义,std::for_each
无法执行此操作。
这是一篇陈旧但仍然有效的参考文章:http://www.drdobbs.com/effective-standard-c-library-foreach-vs/184403769
它说:
例如,使迭代器或序列无效 算法工作在任何情况下都是灾难性的。甚至功能 提供给for_each的对象必须遵守这个常识规则,即使 标准没有这样说。
解释我曾读过的一些明智的话:
C ++标准是由人类写成的;它并不总是像我们希望的那样完美。
答案 2 :(得分:2)
您的代码是合法的。
使用发布的代码,巧合是合法的。如果再向列表中添加一个项目,程序将崩溃。
请尝试以下方法查看问题:
int main()
{
std::list<int> list;
list.push_front(5);
list.push_front(10);
list.push_front(12);
auto it = list.end();
it--; // point to 5
std::for_each(list.begin(), list.end(), [&](int i){
/* the line below will remove the last element in the list;
* list will have only one element (the currently processed one);
* list.end() is not invalidated and we exit for_each() */
list.erase(it);
});
}
答案 3 :(得分:2)
25.2.4 / 2:
效果:将f应用于解除引用中每个迭代器的结果 范围[第一个,最后一个],从第一个开始到最后一个 - 1.
然后25.2.4 / 4:
复杂性:恰好适用于最后一次 - 第一次。
我准备争辩说这两点明确禁止以改变元素数量的方式改变容器。
那就是说,如果列表中还有一个项目,或者如果你的标准库实现在调用你的函数之前增加了迭代器,那么即使你的例子也没有失败(我也看不到任何东西)禁止这种实施的标准。)
答案 4 :(得分:-1)
是的,这是合法的。如果您需要保证不修改底层容器的东西,那就是常数。