gotchacppCritical
Is there a difference between copy-initialization and direct-initialization?
Viewed 0 times
andbetweendirectdifferencecopythereinitialization
Problem
Suppose I have this function:
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.
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
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
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:
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
Notice how I rewrote the conversion function: The parameter type reflects the type of the
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
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
xto an object of typeT. (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 candidatesB(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.