#include <iostream>
usingnamespace std;
class C{
int id;
public:
C(int _id=0) : id(_id) {cout << "construct " << id << endl;}
C(const C& other) : id(other.id+100) {cout << "copy " << id << endl;}
C& operator= (const C& other){id=other.id+1000; cout << "assign " << id << endl;}
~C(){cout << "destruct " << id << endl;}
};
C gimme_C(){
C a(4);
return a;
C b; return b;
}
int main()
{
C x = C(gimme_C());
}
When I run it, the output is as follows:
construct 4
copy 104
destruct 4
destruct 104
When I change the program by commenting out the last line in function gimme_C, "C b; return b;", then the output is this:
construct 4
destruct 4
I'm quite puzzled. I was assuming that commenting out that line would not change the semantics of the program, because that line is never reached. But it does change the output, and therefore it must have somehow changed the semantics. Who can explain?
Normally, in today's C++, when you're returning something by value, move is performed
Your class declares a copy constructor and does not declare a move constructor, which disables the move, so the compiler falls back on return value optimization (specifically, Named Return Value Optimization, NRVO). With RVO, line 14 actually constructs the object x in main()'s function scope, so only one constructor is ever called. That's what you saw in your second test.
However, when you have multiple return points, your compiler (as well as many other compilers) disables RVO because it doesn't know for sure which line in gimme_C should construct main()'s x.
With RVO disabled, it falls back to actually performing a copy on return.
But anyway, I believe the issue is that with the lines there, your compiler isn't optimizing away the temporary a inside gimme_C or the one on line 21. I noticed that if you remove the C() around line 21, I get the same results with or without C b; etc commented out.
I find it a little disturbing that the compiler apparently has a lot of freedom in how to make such choices: it can use RVO or choose not to; and my compiler produces a different program from firedraco's compiler, from the same source code. Isn't there some detailed unambiguous language semantics specification that makes the resulting semantics compiler-independent? I'm fine with optimizations being compiler-dependent, but I'm inclined to expect more uniformity of the semantics, i.e. the output of the program.
I'm inclined to expect more uniformity of the semantics, i.e. the output of the program.
Then don't make the output of the program dependent on the side effects of copy constructors (and also don't make the copy different from the original, as in the example above) A program that does so has unspecified behavior, and that is the C++ language semantics.