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

αλε*_*λυτ 11 c++ undefined-behavior language-lawyer c++11 c++14

关于评估顺序的主题说,在C++ 17之前,以下代码会导致未定义的行为:

a[i] = i++;
Run Code Online (Sandbox Code Playgroud)

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

C++ 14标准1.9/15说:

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

但是如果我们使用std::vector它的iterator对象而不是标量对象i呢?

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

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

das*_*ght 15

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

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

it.operator++(0).operator*();
Run Code Online (Sandbox Code Playgroud)

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

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

struct foo {
    typedef int* iterator;
    iterator begin() { return data; }
private:
    int data[10];
};
Run Code Online (Sandbox Code Playgroud)

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

foo f;
auto it = f.begin();
*it = *it++; // <<== This is UB
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 难道你不是假设C++ 17规则"(复合 - )赋值的左侧是在右侧之后排序吗?当然,由于不确定的排序没有未定义的行为,它只是未指定的行为,但仍然. (2认同)