修改std :: for_each中的容器

时间:2016-02-08 19:19:35

标签: c++ language-lawyer

标准是否明确禁止在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);
});

这绝对是一个糟糕的代码。但这是合法的吗?

5 个答案:

答案 0 :(得分:3)

  

标准是否明确禁止在std::for_each内修改容器?

我唯一能想到的就是这个代码不符合标准的是[alg.foreach]我们有

  

复杂性:正好flast - first

f是函数for_each适用。

由于修改了列表并删除了一个元素,我们不再满足这种复杂性。我不知道是否会使它不符合要求,但我唯一能看到的是,在使用for_each

时,不允许您从容器中删除元素

答案 1 :(得分:3)

  

标准是否明确禁止修改其中的容器   std::for_each

不,不是明确。至少对于“明确”的任何合理定义。关于std::for_each(第25.2.4节)的部分没有说明导致修改迭代范围的迭代器失效或副作用。

但是,有很多隐含原因导致您的代码无法正常工作。考虑一下std::for_each必须如何实施。它完全是通用的,因此对它所运行的std::list一无所知;你给了它两个迭代器来形成一个范围,这意味着可以从last到达firststd::for_each应该如何知道您的函数对象使用firstlast的本地迭代器对象无效?

即使函数知道失效已经发生,它应该怎么办?在手写循环中,您将获取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)

是的,这是合法的。如果您需要保证不修改底层容器的东西,那就是常数。