开发者

Why is copy constructor not being called in this code

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜