Question about copy constructor in return value

114 views Asked by At

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?

1

There are 1 answers

6
Ted Lyngmo On BEST ANSWER

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.