Copy semantics elision and move not working as expected
I made a program to evaluate the performance differences between this:
func3(func2(func1()));
vs this:
retval1 = func1(); retval2 = func2(retval1); func3(retval2);
I prefer the latter for readability and ease of debugging, and I wanted to know if the compiler (MSVC 12.0) would optimize intermediate objects in the release build. My test program is like this:
#include <iostream>
using namespace std;
struct Indicator {
Indicator() {cout << "Default constructor" << endl;}
Indicator(const Indicator& other) {cout << "Copy constructor" << endl;}
const Indicator& operator=(const Indicator& other) {cout << "Assignment operator" << endl;}
~Indicator() {cout << "Destructor" << endl;}
};
Indicator func1()
{return Indicator();}
Indicator func2(Indicator&& i)
{return std::move(i);}
Indicator func3(Indicator&& i)
{return std::move(i);}
int main() {
Indicator i = func3(func2(func1()));
cout << &i << endl;
return 0;
}
I was surprised to see that even with -O2, three more instances are created Indicator
:
Default constructor
Copy constructor
Copy constructor
Destructor
Destructor
00000000002EFC70
Destructor
Press <RETURN> to close this window...
This goes against my understanding of move semantics, which is that only one instance is created in this case Indicator
. I also thought that the compiler should be able to use NRVO for chaining function calls. Can someone explain to me what is going on here?
As per rule 5 , when you defined your copy constructor and copy assignment operator, you disabled the compiler-generated move constructor and moved the assignment operator.
If you define your move constructor, you get the expected result.
Indicator(Indicator&& other) {cout << "Move constructor" << endl;}
Indicator& operator=(Indicator&& other) {cout << "Move assignment operator" << endl;}
Working demo