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

C++ 迭代器和标量对象之间的未定义行为有什么区别吗?,c++,c++11,c++14,language-lawyer,undefined-behavior,C++,C++11,C++14,Language Lawyer,Undefined Behavior,关于求值顺序,在C++17之前,以下代码会导致未定义的行为: a[i] = i++; 这是由于在计算赋值表达式的左侧和右侧部分时未指定顺序造成的 C++14标准1.9/15规定: 如果标量对象上的一个副作用相对于同一标量对象上的另一个副作用或使用同一标量对象的值进行的值计算未排序,并且它们不是潜在并发的(1.10),则该行为未定义 但是如果我们使用std::vector及其迭代器对象而不是标量对象i,会怎么样 std::vector<int> v = {1, 2}; auto it

关于求值顺序,在C++17之前,以下代码会导致未定义的行为:

a[i] = i++;
这是由于在计算赋值表达式的左侧和右侧部分时未指定顺序造成的

C++14标准1.9/15规定:

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

但是如果我们使用
std::vector
及其
迭代器
对象而不是标量对象
i
,会怎么样

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

是否存在未定义的行为(在c++17之前?

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

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

it.operator++(0).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

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

迭代器被允许是原始指针,可能会给您带来麻烦。如果它是一个类对象,那么操作符就是引入序列点的函数调用(在所有标准版本中)。这是特定于向量的,指针将作为迭代器工作。在已知的实现中,部分原因是不允许调试迭代器存储额外的状态。而且,即使
*it=*it++
会起作用,它可能会做
*it=*it++它并且无论如何都不是很有用。大多数边界代码都是这样的——很难看到它做了什么,而且只有很少的用处。如果vector的任何可行实现都可以有一个迭代器,该迭代器会产生未定义的行为,那么该行为通常是未定义的。未定义行为的一个可能结果是一个实现选择,其中的结果实际上定义得很好。我不知道。它确实引入了一个函数调用,使得在调用
f
之前对
*It++
进行全面评估。然而,在C++17之前,我怀疑左侧仍然可以在增量之前或之后进行计算。所以我从来没有写过这样的代码不是一个好主意。C++17只是排除了鼻魔,并保证将
old\u i
分配给
a[old\u i]
a[new\u i]
。在某些情况下,任何一种顺序都是正确的,而让编译器来选择是您想要的。(例如,
f(++i,+++i)
其中
f(a,b)
是可交换的。)C++17允许您安全地编写。但是
n=++i+i在C++17中仍然完全是UB。如果我在右侧使用
f(*it++)
,UB是否会在原始指针实现中消失?@alexolute是的,参数表达式求值的所有副作用都会在函数输入之前生效。您不是假设C++17规则“左侧(复合-)赋值是在右侧?之后排序的。这只是由于不确定的排序而导致的未指定行为,不是未定义的行为,但仍然是。@dasblinkenlight-Hmm,但左侧和右侧的求值顺序仍然是未排序的。即,
*它
(lhs)可以在右侧的函数调用之前或之后求值。因此仍然是UB。我说的对吗?
std::vector<int> v = {1, 2};
auto it = v.begin();
*it = *it.operator++(0);