c++ - C++中赋值语句的求值顺序

标签 c++ language-lawyer operator-precedence

map<int, int> mp;
printf("%d ", mp.size());
mp[10]=mp.size();
printf("%d\n", mp[10]);

此代码产生的答案不是很直观:

0 1

我明白为什么会这样 - 赋值的左侧返回对 mp[10] 的基础值的引用,同时创建上述值,然后才对右侧求值,使用 map 的新计算的 size()

此行为是否在 C++ 标准中的任何地方进行了说明?或者评估顺序未定义?

结果是使用 g++ 5.2.1 获得的。

最佳答案

是的,这包含在标准中并且是未指定的行为。最近的 C++ 标准提案涵盖了这种特殊情况:N4228: Refining Expression Evaluation Order for Idiomatic C++它试图细化评估规则的顺序,使其在某些情况下得到很好的指定。

对这个问题的描述如下:

Expression evaluation order is a recurring discussion topic in the C++ community. In a nutshell, given an expression such as f(a, b, c), the order in which the sub-expressions f, a, b, c are evaluated is left unspecified by the standard. If any two of these sub-expressions happen to modify the same object without intervening sequence points, the behavior of the program is undefined. For instance, the expression f(i++, i) where i is an integer variable leads to undefined behavior , as does v[i] = i++. Even when the behavior is not undefined, the result of evaluating an expression can still be anybody’s guess. Consider the following program fragment:

#include <map>

int main() {
  std::map<int, int>  m;
  m[0] = m.size(); // #1
}

What should the map object m look like after evaluation of the statement marked #1? { {0, 0 } } or {{0, 1 } } ?

我们知道,除非指定,否则子表达式的计算是无序的,这来自 draft C++11 standard 1.9 程序执行部分说:

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.[...]

5.17 赋值和复合赋值运算符 [expr.ass] 的所有部分都是:

[...]In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.[...]

所以这部分没有明确计算的顺序,但我们知道这不是未定义的行为,因为 operator []size() 都是函数调用和部分1.9 告诉我们(强调我的):

[...]When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [ Note: Value computations and side effects associated with different argument expressions are unsequenced. —end note ] Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.9[...]

请注意,我在问题 Does this code from “The C++ Programming Language” 4th edition section 36.3.6 have well-defined behavior? 中介绍了 N4228 提案中的第二个有趣示例.

更新

似乎是 N4228 的修订版 accepted by the Evolution Working Group at the last WG21 meeting但是论文(P0145R0)还没有。所以在 C++17 中这可能不再是未指定的。

更新2

p0145 的第 3 次修订指定并更新 [expr.ass]p1 :

The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand; their result is an lvalue referring to the left operand. The result in all cases is a bit-field if the left operand is a bit-field. In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. The right operand is sequenced before the left operand. ...

关于c++ - C++中赋值语句的求值顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63715601/

相关文章:

c++ - 析构函数和链表

c++ - C/C++ 链接约定

C++20 指定初始化器 char[]

python - 我如何解析这种类型的表达式?

ruby - 为什么 `x =! 5` 返回 false?

c - 了解字符串指针数组

c++ - 下面的代码片段在发布版本中发生了什么?

c++ - 通过 MS RPC 发送 wstring 和 ptime

c++ - 如何理解 "C++ allows sizeof(char*) != sizeof(int*)"?

c++ - C++ 类中定义的友元函数的成员访问控制