Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Copy constructor is NOT called to copy the temporary object to the new defined object

#include <iostream>
using namespace std;

class Y {
public:
    Y(int ) {
        cout << "Y(int)\n";
    }
    Y(const Y&) {
        cout << " Y(const Y&)\n";
    }
};

int main() {
    Y obj1 = 2; // Line 1
}

Output: Y(int)

Expected Output: Y(int) Y(const Y&)

Question> Based on my understanding, Line 1 will first create a temporary object Y(2), and then assign the temporary object to obj1. Thus, I expect both Y(int) and Y(const Y&) are called. But the output from vs2010 only reports the first one(i.e. Y(int)). Why?

like image 236
q0987 Avatar asked May 25 '13 13:05

q0987


3 Answers

Why?

Because under certain conditions (specified by paragraph 12.8/31 of the C++11 Standard) calls to the copy constructor or move constructor can be elided even though those special functions (or the destructor) have side effects:

This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

— [...]

— 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

— [...]

This is the only exception to the so-called "as-if" rule, which normally constrains the kind of transformations (optimizations) that a compiler can perform on a program in order to preserve its observable behavior.

Notice, that the above mechanism is called copy elision - even when it's actually a call to the move constructor that is being elided.

like image 104
Andy Prowl Avatar answered Sep 28 '22 11:09

Andy Prowl


This is because the constructor has only one parameter and it's not marked explicit, so the compiler automatically turns:

Y obj1 = 2;

Into:

Y obj1(2);

To prevent this behaviour, use:

explicit Y(int ) {
    cout << "Y(int)\n";
}

(and compilation will fail in your case).

like image 25
Stefano Sanfilippo Avatar answered Sep 28 '22 10:09

Stefano Sanfilippo


This is called copy initialization. Y(int ) is a conversion constructor. that is

single-parameter constructor that is declared without the function specifier explicit

the compiler is allowed to elide the extra copy and use your conversion constructor. Which means

Y obj1 = 2; // Line 1

is equivalent to

Y obj1(2); // Line 1
like image 36
stardust Avatar answered Sep 28 '22 11:09

stardust