I made a program to assess the performance differences between doing 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 away the intermediate objects in a release build. My test program is 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, there are still three instances of Indicator
being created:
Default constructor
Copy constructor
Copy constructor
Destructor
Destructor
00000000002EFC70
Destructor
Press <RETURN> to close this window...
This conflicts with my understanding of move semantics, which is that there should only be one instance of Indicator
created in this case. I also thought that the compiler should be able to use NRVO for the chained function calls. Can someone explain to me what's going on here?