Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Order of evaluation of assignment statement in C++

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

This code yields an answer that is not very intuitive:

0 1

I understand why it happens - the left side of the assignment returns reference to mp[10]'s underlying value and at the same time creates aforementioned value, and only then is the right side evaluated, using the newly computed size() of the map.

Is this behaviour stated anywhere in C++ standard? Or is the order of evaluation undefined?

Result was obtained using g++ 5.2.1.

like image 407
akrasuski1 Avatar asked Nov 08 '15 20:11

akrasuski1


People also ask

What is the typical evaluation order for an assignment expression?

..., when evaluating the operands of an expression, assignment, or return statement, all function calls, method calls, and (channel) communication operations are evaluated in lexical left-to-right order.

What is the order of evaluation?

Order of evaluation refers to the operator precedence and associativity rules according to which mathematical expressions are evaluated.

Which side of an assignment statement is evaluated first?

The assignment expression evaluates the operand on the right side of the operator and places its value in the variable on the left. When a compound assignment is used with an expression, the expression is only evaluated first when parentheses are used to raise its level of precedence.


1 Answers

Yes, this is covered by the standard and it is unspecified behavior. This particular case is covered in a recent C++ standards proposal: N4228: Refining Expression Evaluation Order for Idiomatic C++ which seeks to refine the order of evaluation rules to make it well specified for certain cases.

It describes this problem as follows:

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 } } ?

We know that unless specified the evaluations of sub-expressions are unsequenced, this is from the draft C++11 standard section 1.9 Program execution which says:

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

and all the section 5.17 Assignment and compound assignment operators [expr.ass] says is:

[...]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.[...]

So this section does not nail down the order of evaluation but we know this is not undefined behavior since both operator [] and size() are function calls and section 1.9 tells us(emphasis mine):

[...]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[...]

Note, I cover the second interesting example from the N4228 proposal in the question Does this code from “The C++ Programming Language” 4th edition section 36.3.6 have well-defined behavior?.

Update

It seems like a revised version of N4228 was accepted by the Evolution Working Group at the last WG21 meeting but the paper(P0145R0) is not yet available. So this could possibly no longer be unspecified in C++17.

Update 2

Revision 3 of p0145 made this specified and update [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. ...

like image 169
Shafik Yaghmour Avatar answered Oct 16 '22 08:10

Shafik Yaghmour