HiveBrain v1.2.0
Get Started
← Back to all entries
gotchacppCritical

Is there a difference between copy-initialization and direct-initialization?

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
andbetweendirectdifferencecopythereinitialization

Problem

Suppose I have this function:

void my_test()
{
    A a1 = A_factory_func();
    A a2(A_factory_func());

    double b1 = 0.5;
    double b2(0.5);

    A c1;
    A c2 = A();
    A c3(A());
}


In each grouping, are these statements identical? Or is there an extra (possibly optimizable) copy in some of the initializations?

I have seen people say both things. Please cite text as proof. Also add other cases please.

Solution

C++17 Update

In C++17, the meaning of A_factory_func() changed from creating a temporary object (C++

  • Copy initialization constructs an implicit conversion sequence: It tries to convert x to an object of type T. (It then may copy over that object into the to-initialized object, so a copy constructor is needed too - but this is not important below)



As you see, copy initialization is in some way a part of direct initialization with regard to possible implicit conversions: While direct initialization has all constructors available to call, and in addition can do any implicit conversion it needs to match up argument types, copy initialization can just set up one implicit conversion sequence.

I tried hard and got the following code to output different text for each of those forms, without using the "obvious" through explicit constructors.

#include 
struct B;
struct A { 
  operator B();
};

struct B { 
  B() { }
  B(A const&) { std::cout  "; }
};

A::operator B() { std::cout  "; return B(); }

int main() { 
  A a;
  B b1(a);  // 1)
  B b2 = a; // 2)
}
// output:  


How does it work, and why does it output that result?

-
Direct initialization

It first doesn't know anything about conversion. It will just try to call a constructor. In this case, the following constructor is available and is an exact match:

B(A const&)


There is no conversion, much less a user defined conversion, needed to call that constructor (note that no const qualification conversion happens here either). And so direct initialization will call it.

-
Copy initialization

As said above, copy initialization will construct a conversion sequence when a has not type B or derived from it (which is clearly the case here). So it will look for ways to do the conversion, and will find the following candidates

B(A const&)
 operator B(A&);


Notice how I rewrote the conversion function: The parameter type reflects the type of the this pointer, which in a non-const member function is to non-const. Now, we call these candidates with x as argument. The winner is the conversion function: Because if we have two candidate functions both accepting a reference to the same type, then the less const version wins (this is, by the way, also the mechanism that prefers non-const member function calls for non-const objects).

Note that if we change the conversion function to be a const member function, then the conversion is ambiguous (because both have a parameter type of A const& then): The Comeau compiler rejects it properly, but GCC accepts it in non-pedantic mode. Switching to -pedantic makes it output the proper ambiguity warning too, though.

Code Snippets

A a1 = A_factory_func();
A a2(A_factory_func());
double b1 = 0.5;
double b2(0.5);
A c1;
A c2 = A();
A c3(A());
T t(x);
T t = x;
#include <iostream>
struct B;
struct A { 
  operator B();
};

struct B { 
  B() { }
  B(A const&) { std::cout << "<direct> "; }
};

A::operator B() { std::cout << "<copy> "; return B(); }

int main() { 
  A a;
  B b1(a);  // 1)
  B b2 = a; // 2)
}
// output: <direct> <copy>

Context

Stack Overflow Q#1051379, score: 298

Revisions (0)

No revisions yet.