Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy constructor not called when initializing an object with return value of a function

Consider the following code:

#include <iostream>

using namespace std;

class A
{
    public:
        int a;
        A(): a(5)
        {
           cout << "Constructor\n";
        }
        A(const A &b)
        {
            a = b.a;
            cout << "Copy Constructor\n";
        }
        A fun(A a)
        {
            return a;
        }
};

int main()
{
    A a, c;
    A b = a.fun(c);
    return 0;
}

The output of the above code with g++ file.cpp is:

Constructor
Constructor
Copy Constructor
Copy Constructor

The output of the above code with g++ -fno-elide-constructors file.cpp is:

Constructor
Constructor
Copy Constructor
Copy Constructor
Copy Constructor

I know Return Value Optimization. My question is which call to copy constructor is elided(temporary object during returning or returned object being copied to b)?

If the elided copy constructor is the one used for creating b, then how is b created at all (because there is no constructor call in this case also)?

If I replace the line A b = a.fun(c); with a.fun(c) and compile using the first method or even the second method, then also the copy constructor is being called 2 times . So, if in the case explained in the previous paragraph, the temporary object's copy constructor is elided, then why isn't it elided in this case?

like image 714
crisron Avatar asked Nov 14 '14 19:11

crisron


People also ask

Why is my copy constructor not being called?

It's because of copy elision optimization by the compiler. Adding -fno-elide-constructors option to g++ while compiling will disable that optimization.

Does return by value call copy constructor?

The copy constructor is called because you call by value not by reference. Therefore a new object must be instantiated from your current object since all members of the object should have the same value in the returned instance.

How is a copy constructor related to a function returning an object?

The function is about to return. The copy constructor is invoked. It takes a reference to the local variable. It uses this reference to copy everything into the new object that will be used as the return value.

What is a copy constructor a constructor to initialize an object with the values of another object?

d) Initialize all the objects of a class to another object of another class. Explanation: The copy constructor has the most basic function to initialize the members of an object with same values as that of some previously created object. The object must be of same class.


2 Answers

#include <iostream>

using namespace std;

class A
{
public:
    int a;
    A(): a(5)
    {
        cout << "Constructing: " << (void *)this << std::endl;
    }
    A(const A &b)
    {
        a = b.a;
        cout << "Copy Constructor: " << (void *)this << " from " << (void *)&b << std::endl;
    }
    A fun(A a)
    {
        return a;
    }
};

int main()
{

    A a, c;
    A b = a.fun(c);

    std::cout << "a:" << (void *)&a << std::endl <<
              "b:" << (void *)&b << std::endl <<
              "c:" << (void *)&c << std::endl;
    return 0;
}

Yields:

Constructing: 0x7fffbb377220
Constructing: 0x7fffbb377210
Copy Constructor: 0x7fffbb377230 from 0x7fffbb377210
Copy Constructor: 0x7fffbb377200 from 0x7fffbb377230
a:0x7fffbb377220
b:0x7fffbb377200
c:0x7fffbb377210

So it constructs a, constructs c, copies c to an intermediate (argument a of the function), and then copies the intermediate directly into b, skipping the typical copying of a to a return intermediate. This is even better demonstrated if you pass by value (change to A fun(const A& a):

Constructing: 0x7fff8e9642b0
Constructing: 0x7fff8e9642a0
Copy Constructor: 0x7fff8e964290 from 0x7fff8e9642a0
a:0x7fff8e9642b0
b:0x7fff8e964290
c:0x7fff8e9642a0

a is constructed, c is constructed, c is copied directly to b, despite b not being passed to fun!

like image 153
IdeaHat Avatar answered Sep 30 '22 18:09

IdeaHat


The copy that is elided is the copy of the temporary return value into b. Without elision the return value is initialized from a and copied to b. Instead, the temporary that would otherwise hold the return value is constructed into b and initialized with a. [class.copy]/31:

when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move

You can observe this if you add an additional output in fun:

A fun(A a)
{
    cout << "fun!" << endl;
    return a;
}

Then with the elision you'll get

[…]
fun!
Copy Constructor

And without:

[…]
fun!
Copy Constructor
Copy Constructor

like image 42
Columbo Avatar answered Sep 30 '22 16:09

Columbo