So why is Copy constructor not being invoked in "const Integer operator+(const Integer &rv)" function. Is it because of RVO. If Yes what do I need to do to prevent it?
#include <iostream>
using namespace std;
class Integer {
int i;
public:
Integer(int ii = 0) : i(ii) {
cout << "Integer()" << endl;
}
Integer(const Integer &I) {
cout << "Integer(const Integer &)" << endl;
}
~Integer() {
cout << "~Integer()" << endl;
}
const Integer operator+(const Integer &rv) const {
cout << "operator+" << endl;
Integer I(i + rv.i);
I.print();
return I;
}
Integer &operator+=(const Integer &rv) {
cout << "operator+=" << endl;
i + rv.i;
return *this;
}
void print() {
cout << "i:开发者_运维技巧 " << i << endl;
}
};
int main() {
cout << "built-in tpes:" << endl;
int i = 1, j = 2, k = 3;
k += i + j;
cout << "user-defined types:" << endl;
Integer ii(1), jj(2), kk(3);
kk += ii + jj;
}
I do get an error If I'll comment out copy constructor. I'm expecting copy constructor to be called when operator+ returns. Following is the output of the program
built-in tpes:
user-defined types:
Integer()
Integer()
Integer()
operator+
Integer()
i: 3 // EXPECTING Copy Constructor to be called after this
operator+=
~Integer()
~Integer()
~Integer()
~Integer()
Is it because of RVO. If Yes what do I need to do to prevent it?
Yes. But it didn't get called because of Return Value Optimization by the compiler.
If you're using GCC, then use -fno-elide-constructors
option to avoid it.
GCC 4.6.1 manual says,
-fno-elide-constructors
The C++ standard allows an implementation to omit creating a temporary which is only used to initialize another object of the same type. Specifying this option disables that optimization, and forces G++ to call the copy constructor in all cases.
(N)RVO is one of the easiest to implement optimizations. In most calling conventions for return by value the caller reserves the space for the returned object and then passes a hidden pointer to the function. The function then constructs the object in the address that is given. That is, kk += ii + jj;
is translated into something like:
Integer __tmp;
// __rtn this arg
Integer::operator+( &tmp, &ii, jj );
kk += __tmp;
The function (in this case Integer::operator+
takes a first hidden argument __rtn
that is a pointer to an uninitialized block of memory of sizeof(Integer)
bytes, where the object is to be constructed, a second hidden argument this
, and then the argument to the function in the code.
Then the implementation of the function is translated into:
Integer::operator+( Integer* __rtn, Integer const * this, const Integer &rv) {
cout << "operator+" << endl;
new (__rtn) Integer(i + rv.i);
__rtn->print();
}
Because the calling convention passes the pointer, there function does not need to reserve extra space for a local integer that would then be copied, as it can just build the I
in your code straight into the received pointer, and avoid the copy.
Note that not in all circumstances the compiler can perform NRVO, in particular, if you have two local objects in the function and you return either one depending on a condition that is not inferable from the code (say the value of an argument to the function). While you could do that to avoid RVO, the fact is that it will make your code more complex, less efficient and harder to maintain.
精彩评论