AK2806 AK2806 - 16 days ago 5
C++ Question

Is the expression MyClass a = b + c a copy initialization?

I know the expression like MyClass a = b is a copy initialization and it'll call the copy constructor.

What about MyClass a = b + c?

I have tried it. In visual studio 2015, it seems the expression is not a copy initialization nor a copy assignment.So what is it?

Below is the code I tested:

class MyClass
{
public:
MyClass()
{
cout << "default constructor has been called!" << endl;
}
MyClass(const MyClass& cls)
{
cout << "copy constructor has been called!" << endl;
}
MyClass operator+(const MyClass& cls) const
{
cout << "operator + has been called!" << endl;
return MyClass();
}
MyClass& operator=(const MyClass& cls)
{
cout << "operator = has been called!" << endl;
return *this;
}
};

int main()
{
MyClass b, c;
MyClass a = b + c;
return 0;
}


And the output is

default constructor has been called!
default constructor has been called!
operator + has been called!
default constructor has been called!

Answer

What you experience is called return value optimization (see What are copy elision and return value optimization?).

The compiler optimizes code (to minimize objects created) and makes a actually be the local object declared and returned by your+ operator. Then, there is no copy constructor used. This is what you observe here.

To nicely observe this, change your code to see object memory adresses:

#include <iostream>
using namespace std;

class MyClass
{
public:
    MyClass()
    {
        cout << "default constructor has been called for " << std::hex << this << endl;
    }
    MyClass(const MyClass& cls)
    {
        cout << "copy constructor has been called for " << std::hex << this << " (copied from " << &cls << ")" << endl;
    }
    MyClass operator+(const MyClass& cls) const
    {
        cout << "operator + has been called!" << endl;
        MyClass res;
        cout << "operator + returns temporary object " << std::hex << &res << endl;
        return res;
    }
    MyClass& operator=(const MyClass& cls)
    {
        cout << "operator = has been called!" << endl;
        return *this;
    }
};

int main()
{
    MyClass b, c;
    MyClass a = b + c;
    cout << "a object is " << std::hex << &a << endl;
    return 0;
}

It outputs:

default constructor has been called for 0x7ffd44b769fd
default constructor has been called for 0x7ffd44b769fe
operator + has been called!
default constructor has been called for 0x7ffd44b769ff
operator + returns temporary object 0x7ffd44b769ff
a object is 0x7ffd44b769ff

You see that temporary object created in operator+ is the same as a (0x7ffd44b769ff), due to compiler optimization.

If you use g++, compile with -fno-elide-constructors to disable this compiler optimization. And the output is now:

default constructor has been called for 0x7ffd92847d1c
default constructor has been called for 0x7ffd92847d1d
operator + has been called!
default constructor has been called for 0x7ffd92847cff
operator + returns temporary object 0x7ffd92847cff
copy constructor has been called for 0x7ffd92847d1f (copied from 0x7ffd92847cff)
copy constructor has been called for 0x7ffd92847d1e (copied from 0x7ffd92847d1f)
a object is 0x7ffd92847d1e

You see that now operator+ creates a local object (0x7ffd92847cff), which is later copied as a temporary object for return statement (copying 0x7ffd92847cff to 0x7ffd92847d1f), which is finally used to construct a by copy (copying 0x7ffd92847d1f to 0x7ffd92847d1e).