迭代器和标量对象之间的未定义行为有什么区别吗?

时间:2017-08-19 10:21:59

标签: c++ c++11 c++14 language-lawyer undefined-behavior

关于评估顺序的

The topic表示以下代码会导致未定义的行为,直到C ++ 17:

a[i] = i++;

这是由于在评估赋值表达式的左右部分时未指定的顺序而发生的。

C ++ 14标准1.9 / 15说:

  

如果对标量对象的副作用相对于同一标量对象的另一个副作用或使用相同标量对象的值的计算未被排序,并且它们不可能并发(1.10),则行为为未定义。

但是如果我们使用std::vector及其iterator对象而不是标量对象i会怎样?

std::vector<int> v = {1, 2};
auto it = v.begin();
*it = *it++;   // UB?

是否存在未定义的行为(直到c ++ 17)?

1 个答案:

答案 0 :(得分:15)

在迭代器是类的情况下,行为在标准的所有版本中都得到了很好的定义,假设it++指向其容器内的有效位置(在您的示例中它就是这样)。

C ++将*it++转换为这两个函数调用的序列:

it.operator++(0).operator*();

函数调用引入了排序,因此在++内部调用的实际operator++对用作迭代器实现的原语(可能是原始指针)的所有副作用必须在功能退出。

但是,迭代器不需要是类:它们也可以是指针:

struct foo {
    typedef int* iterator;
    iterator begin() { return data; }
private:
    int data[10];
};

代码看起来一样,并且继续编译,但现在行为未定义:

foo f;
auto it = f.begin();
*it = *it++; // <<== This is UB

您可以通过调用++作为成员函数来防范这种情况:

std::vector<int> v = {1, 2};
auto it = v.begin();
*it = *it.operator++(0);

当迭代器实际上是指针时,此代码将无法编译,而不是导致未定义的行为。