#include <iostream>
int& addOne(int& x)
{
x += 1;
return x;
}
int main()
{
int x {5};
addOne(x) = x;
std::cout << x << ' ' << addOne(x);
}
I'm currently in the middle of learning about lvalues and rvalues and was experimenting a bit, and made this which seems to be getting conflicting results. https://godbolt.org/z/KqsGz3Toe produces an out put of "5 6", as does Clion and Visual Studio, however https://www.onlinegdb.com/49mUC7x8U produces a result of "6 7"
I would think that because addOne is calling x as a reference, it would explicitly change the value of x to 6 despite being called as an lvalue. What should the correct result be?
Since C++17 the order of evaluation is specified such that the operands of
=are evaluated right-to-left and those of<<are evaluated left-to-right, matching the associativity of these operators. (But this doesn't apply to all operators, e.g.+and other arithmetic operators.)So in
first the value of the right-hand side is evaluated, yielding
5. Then the functionaddOneis called and it doesn't matter what it does withxsince it returns a reference to it, to which the right-hand value5is assigned.Formally, evaluating the right-hand side first means that we replace the lvalue
xby the (pr)value it holds (lvalue-to-rvalue conversion). Then we calladdOne(x)to modify the object that the lvaluexrefers to.So, imagining temporary variables to hold the results of the individual evaluations, the line is equivalent to (except for extra copies introduced by the new variables, which don't matter in the case of
int):Then in the line
we first evaluate and output
x, resulting in5, and then calladdOne, resulting in6.So the line is equivalent to (simplified, knowing that
operator<<will returnstd::coutagain):The output
5 6is the only correct one since C++17.Before C++17, the evaluation order of the two sides of the assignment operator was unsequenced.
Having a scalar modification unsequenced with a value computation on the same scalar (on the right-hand side of your assignment) causes undefined behavior normally.
But since you put the increment of
xinto a function, an additional rule saying that the execution of a function body is merely indeterminately sequenced with other evaluations in the calling context saves this. It means that the line wont have undefined behavior anymore, but the order in which the evaluations of the two sides of the assignment happen could be either left-first or right-first.This means we won't know whether
xis evaluated first and thenaddOne(x)or the other way around.Therefore after the line,
xmay be5or6.6would be obtained if the evaluation happened equivalently toThen in the line
pre-C++17 the same issue applied. The evaluations of the arguments to
<<were indeterminately sequenced, rather than left-to-right and soaddOne(x)could be evaluated before the left-handx, i.e. in addition to the previous order, the evaluation could also be equivalent toIn this case
xis first incremented and then its new value is printed twice.Therefore possible program output could be either of the following:
(Technically the
int t2 = addOne(x)are two evaluations: One call toaddOnereturning a reference and then the lvalue-to-rvalue conversion. These could happen interleaved with the other evaluations, but this doesn't give any new program outputs.)You can specify to use C++17 (or newer versions like C++20) with the
-std=c++17flag to the compiler if you are using GCC or Clang and/std:c++17if you are using MSVC. Which standard version is chosen by-default depends on the compiler and compiler version.