I've been debugging a program for some time, and eventually found the error was due to a reference not being updated as I thought it would be.
Here's a example that shows the problem I encountered:
#include <iostream>
using namespace std;
struct Test {
Test& set(int& i){ i = 10; return *this; }
Test& print(const int& i){ cout << i << endl; return *this; }
};
int main(void){
int i = 0;
Test t;
t.set(i).print(i + 5);
return 0;
}
I had expected that the print() method here would output 15, but instead it outputs 5.
EDIT: 10 days later I just realised that with clang it outputs 15! Is this a bug in GCC?
There's no guarantee in C++ about the order in which function arguments in a single expression are evaluated, not even when these functions are chained method calls. You are invoking undefined behavior here and that's what you get.
The . operator does imply sequencing, but only insofar that the expression before the . has to be fully evaluated before the member is accessed. It doesn't mean that the evaluations of subexpressions are suspended until that point.
Also, don't pass
int
s byconst int&
, there's no way this could be faster than passing anint
directly (unless for some strange reasonint
doesn't fit into a processor word and the reference does).Let me try interpreting the C++11 standard on this. In §1.9/15 it says:
Certainly
int
is an scalar type, andt.set(i).print(i + 5);
contains a side effect oni
inset()
and the value computationi + 5
, so if not noted otherwise, the behavior is indeed undefined. Reading §5.2.5 ("Class member access"), I could not find any notes about sequences regarding the.
operator. [But see edit below!]Note, however that it is of course guaranteed that
set()
is executed beforeprint()
because the latter receives the return value of the former as an (implicitthis
) argument. The culprit here is that the value computation forprint
's arguments isunsequencedindeterminately sequenced relative to the call ofset
.EDIT: After reading the answer in your (@Xeno's) comment, I reread the paragraph in the standard, and in fact it later says:
Because indeterminately sequenced is not unsequenced ("execution of unsequenced evaluations can overlap", §1.9/13), this is indeed not undefined behavior, but "just" unspecified behavior, meaning that both 15 and 5 are correct outputs.
So when
<
means "sequenced before" and~
means "indeterminately sequenced", we have:(value computations for print()'s arguments ~ execution of set()) < execution of print()
[too long for a comment:]
If adding
This call
returns
From this I conclude that the culprit is the
i + 5
as parameter to print.