11

根据这个非常受欢迎的答案,遍历一组擦除某些元素的规范方法如下:

for (it = mySet.begin(); it != mySet.end(); ) {
    if (conditionToDelete(*it)) {
        mySet.erase(it++);
    }
    else {
        ++it;
    }
}

当然,这是 C++03 的 set erase 没有返回迭代器的结果。否则可以写 可以写it = mySet.erase(it);也很明显

itToDelete = it++;
mySet.erase(itToDelete);

这个问题不是关于如何在迭代时删除元素。问题是为什么以下行显然不会导致未定义的行为。

mySet.erase(it++);

起初我确信这一定是 UB,因为我对后增量的想法是错误的。这是一种常见(但错误)的方式,将预增量视为在其余评估之前发生,而后增量发生在之后。当然,这是错误的。postincrement 和 preincrement 都有增加变量的副作用。不同之处在于这些表达式的值。

也就是说,据我所知,C++ 标准(至少是 C++03 标准)没有具体说明后增量的副作用何时发生。所以,除非我们有保证,如果作为后增量表达式的函数参数在进入函数体之前会产生副作用,那么这不应该是 UB 吗?究竟是什么(标准方面)(如果有的话)禁止在迭代器在函数体内无效后发生 it++ 的副作用?

来自标准的报价将非常受欢迎。

为了论证的缘故,我们还假设 set 的迭代器是内置类型,这实际上是 operator ++,而不是重载的 operator-function

4

1 回答 1

11

这不是C++03中未定义的行为,因为有一个序列点因为在计算所有函数参数之后

最接近 C++03 并且可以公开获得的标准草案是N1804,我找不到之前的标准草案的公开版本,但是关于序列点的维基百科文章使用了C++98c++ 03作为参考,短语与N1804的以下段落一致。

1.9 程序执行部分第16段中说(强调我的前进):

调用函数时(无论该函数是否内联),在对所有函数参数(如果有)求值之后都有一个序列点,该序列点发生在函数体中的任何表达式或语句执行之前。[...]

稍后在5.2.2 函数调用8段中说:

参数的评估顺序未指定。参数表达式求值的所有副作用在输入函数之前生效。后缀表达式和参数表达式列表的求值顺序未指定。

于 2013-12-20T15:15:08.827 回答