This is a code test from this. I reproduced some of them as below:
//struct definition. All kinds of prints
struct Snitch { // Note: All methods have side effects
Snitch() { cout << "c'tor" << endl; }
~Snitch() { cout << "d'tor" << endl; }
Snitch(const Snitch&) { cout << "copy c'tor" << endl; }
Snitch(Snitch&&) { cout << "move c'tor" << endl; }
Snitch& operator=(const Snitch&) {
cout << "copy assignment" << endl;
return *this;
}
Snitch& operator=(Snitch&&) {
cout << "move assignment" << endl;
return *this;
}
};
//end struct def
Snitch ReturnObj() {
Snitch snit;
cout<< "before return ReturnObj" << endl;
return snit;
}
int main() {
Snitch snitchobj1 = ReturnObj();
cout<< "end main" << endl;
}
I disabled RVO as the author: g++ -fno-elide-constructors -o rvo.exe .\rvo_or_not.cpp and run rvo.exe in order to find what exactly happens.
I got:
c'tor
before return ReturnObj
move c'tor //I didn't know why this is move c'tor instead of copy c'tor
d'tor
move c'tor
d'tor
end main
d'tor
The first printing of move c'tor is unexpected for me. The line "Snitch snit;" in function defines a local lvalue snit . So when returned, the copy construtor should be called to initialize the temporary object? Am I right? Or it's an xvalue actually not a lvalue?
When returning a local variable by value the compiler will prefer to move it over doing a copy if it can't do named return value optimization, as in your case since you've turned NRVO (named return value optimization) off - URVO (or unnamed RVO) can't be turned off, at least not while in a strict conformance mode.
the copy construtor should be called to initialize the temporary object
There is no temporary object that I can see. The output I get from g++, clang++ and icx in C++17 mode with -fno-elide-constructors
has only one move:
c'tor
before return ReturnObj
move c'tor
d'tor
end main
d'tor
In the link you showed, they compile in C++11 mode:
clang++ -std=c++11 -fno-elide-constructors
Mandatory URVO wasn't a thing until C++17. In C++11 and 14 both URVO and NRVO is allowed but not mandatory. You can turn off both RVO versions in C++11 and 14 with -fno-elide-constructors
, but in C++17 URVO is mandatory and can't be turned off while NRVO can be.