我正在努力理解C++中表达式的求值顺序。我有以下例子:
int i = 0;
std::cin >> i >> i; //IS THIS UB?
我的第一个问题是,上面显示的片段会产生UB吗?
接下来,
int i = 0;
std::cin >> i >> ++i; //IS THIS UB?
我的第二个问题是,这个片段会产生UB吗?
std::cin >> i >> i;
是并且总是定义良好的。
std::cin >> i >> ++i;
现在定义明确,但在C++17之前是UB。
这种UB是由对同一标量的未排序写入或相对于同一标量读取的未排序写操作引起的。
在std::cin >> i >> i;
中,即使i
被修改了两次(在过载的>>
内部(,第一次修改也会在第二次修改之前排序。必须首先计算第一个>>
的返回值,然后将其作为参数传递给第二个>>
,因此必须按此顺序调用它们。请参阅此处的规则(3(。
另一方面,在std::cin >> i >> ++i;
中,在C++17之前,第一个>>
对i
的修饰相对于++i
是无序列的,导致UB。但在C++17及更新版本中,>>
和<<
总是在rhs之前评估lhs,导致第一个>>
在++i
之前测序,请参见此处的规则(19(。
案例I
在这种情况下,std::cin >> i >> i;
可以写成(或等效于(:
std::cin.operator>>(i).operator>>(i);
这里有两件重要的事情需要考虑
std::cin::operator>>
通过引用返回cin
对象- 对CCD_ 19的每次调用都保证按从左到右的顺序进行
所以在这种情况下没有未定义的行为。
情况II
在这种情况下,std::cin >> i >> ++i;
可以写成(或等效于(:
std::cin.operator>>(i).operator>>(++i);
现在,从第1.9节第16点开始:
调用函数(无论函数是否内联(时,在执行函数体中的任何表达式或语句之前,所有函数参数(如果有(的求值之后都有一个序列点。在复制返回值之后,在执行函数外部的任何表达式之前,还有一个序列点。
但请注意,上述报价声明仅保证:
i
在对operator>>
的第一次调用之前进行求值,并且- 在对CCD_ 24的第二次调用之前评估CCD_
但引用不能保证在++i
之前评估i
,反之亦然。这意味着i
和++i
的求值顺序是未定义,因此在这种情况下生成的代码将具有不定义的行为。
上述讨论(顺序点(适用于Pre-C++11,因为OP没有标记C++11。