d512
d512

Reputation: 34113

Why is C++ copy constructor called twice?

I have some code that returns a class object by value and the copy constructor is being called more than I thought it would. This seems to be how the g++ compiler does things but I'm not sure why. Say I have this code:

#include <memory>
#include <iostream>

using namespace std;

class A
{
public:
    A() { cout << "constructor" << endl; }
    A(const A& other) { cout << "copy constructor" << endl; }
    ~A() { cout << "destructor" << endl; }
};

A f()
{
    return A();
}

int main()
{
    cout << "before f()\n";
    A a = f();
    cout << "after f()\n";
    return 0;
}

When compiled with constructor elision turned off

g++ test.cpp -fno-elide-constructors

it outputs this:

before f()
constructor
copy constructor
destructor
copy constructor
destructor
after f()
destructor

So the copy constructor is called twice. I can see why the copy constructor would be called when copying the return value of f() into the variable a but why again?

I've read about C++ compilers returning temporary objects from functions but I don't understand the point of that. Even with elision turned off it seems unnecessary to create a temporary object for every function return value.

Update

Just to be super clear here, I'm not surprised that the copy constructor is being called. I expect that since I turned off elision. What I'm confused about is why the copy constructor needs to be called twice. It seems like once should be enough to copy the result from f() into a.

Upvotes: 1

Views: 705

Answers (2)

user12002570
user12002570

Reputation: 1

Pre-C++17

In Pre-C++17 standard there was non-mandatory copy elison, so by using the -fno-elide-constructors flag you are disabling the return value optimization and some other optimizations where copies are elided.

This is what is happening in your program:

  1. First due to the return statement return A(); an object is constructed using the default constructor A::A().
  2. Next, a copy of that default constructed object is returned to the caller. This is done using the copy constructor A::A(const A&). Hence you get the first copy constructor call output.
  3. Finally, due to the statement A a = f(); which is copy initialization, the object named a is created as a copy of that returned value using the copy constructor A::A(const A&). Hence you get the second copy constructor call output.
//--v----------------------->1st copy constructor called due to this(return by value) 
    A f()
    {
//-------------vvv---------->default constructor called due to this
        return A();
    }
//--vvvvvvvvv--------------->2nd copy constructor called due to this(copy initialization)
    A a = f();

C++17

In C++17(and onwards), due to mandatory copy elison, you will not get any copy constructor call output. Demo.

Upvotes: 5

Ted Lyngmo
Ted Lyngmo

Reputation: 117298

It's because pre C++17, compilers were not mandated to elide the copies. If you do turn on compiling this as C++17 or later, there will only be one constructor call - even if you use -fno-elide-constructors.

Demo

The instances in C++14 with -fno-elide-constructors are created like this:

A // 2:nd (1:st copy)
f()
{
    return A();
    //     1:st
}

    A a = f();
//  3:rd (2:nd copy)

If you instead return {};, you can skip the first temporary instance and only get one copy:

A // 1:st
f()
{
    return {};
    //    arg to ctor
}

    A a = f();
//  2:nd (1:st copy)

Demo

Upvotes: 5

Related Questions