Related
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.
C++17 Update
In C++17, the meaning of A_factory_func() changed from creating a temporary object (C++<=14) to just specifying the initialization of whatever object this expression is initialized to (loosely speaking) in C++17. These objects (called "result objects") are the variables created by a declaration (like a1), artificial objects created when the initialization ends up being discarded, or if an object is needed for reference binding (like, in A_factory_func();. In the last case, an object is artificially created, called "temporary materialization", because A_factory_func() doesn't have a variable or reference that otherwise would require an object to exist).
As examples in our case, in the case of a1 and a2 special rules say that in such declarations, the result object of a prvalue initializer of the same type as a1 is variable a1, and therefore A_factory_func() directly initializes the object a1. Any intermediary functional-style cast would not have any effect, because A_factory_func(another-prvalue) just "passes through" the result object of the outer prvalue to be also the result object of the inner prvalue.
A a1 = A_factory_func();
A a2(A_factory_func());
Depends on what type A_factory_func() returns. I assume it returns an A - then it's doing the same - except that when the copy constructor is explicit, then the first one will fail. Read 8.6/14
double b1 = 0.5;
double b2(0.5);
This is doing the same because it's a built-in type (this means not a class type here). Read 8.6/14.
A c1;
A c2 = A();
A c3(A());
This is not doing the same. The first default-initializes if A is a non-POD, and doesn't do any initialization for a POD (Read 8.6/9). The second copy initializes: Value-initializes a temporary and then copies that value into c2 (Read 5.2.3/2 and 8.6/14). This of course will require a non-explicit copy constructor (Read 8.6/14 and 12.3.1/3 and 13.3.1.3/1 ). The third creates a function declaration for a function c3 that returns an A and that takes a function pointer to a function returning a A (Read 8.2).
Delving into Initializations Direct and Copy initialization
While they look identical and are supposed to do the same, these two forms are remarkably different in certain cases. The two forms of initialization are direct and copy initialization:
T t(x);
T t = x;
There is behavior we can attribute to each of them:
Direct initialization behaves like a function call to an overloaded function: The functions, in this case, are the constructors of T (including explicit ones), and the argument is x. Overload resolution will find the best matching constructor, and when needed will do any implicit conversion required.
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 <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>
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.
Assignment is different from initialization.
Both of the following lines do initialization. A single constructor call is done:
A a1 = A_factory_func(); // calls copy constructor
A a1(A_factory_func()); // calls copy constructor
but it's not equivalent to:
A a1; // calls default constructor
a1 = A_factory_func(); // (assignment) calls operator =
I don't have a text at the moment to prove this but it's very easy to experiment:
#include <iostream>
using namespace std;
class A {
public:
A() {
cout << "default constructor" << endl;
}
A(const A& x) {
cout << "copy constructor" << endl;
}
const A& operator = (const A& x) {
cout << "operator =" << endl;
return *this;
}
};
int main() {
A a; // default constructor
A b(a); // copy constructor
A c = a; // copy constructor
c = b; // operator =
return 0;
}
double b1 = 0.5; is implicit call of constructor.
double b2(0.5); is explicit call.
Look at the following code to see the difference:
#include <iostream>
class sss {
public:
explicit sss( int )
{
std::cout << "int" << std::endl;
};
sss( double )
{
std::cout << "double" << std::endl;
};
};
int main()
{
sss ddd( 7 ); // calls int constructor
sss xxx = 7; // calls double constructor
return 0;
}
If your class has no explicit constuctors than explicit and implicit calls are identical.
You can see its difference in explicit and implicit constructor types when you initialize an object :
Classes :
class A
{
A(int) { } // converting constructor
A(int, int) { } // converting constructor (C++11)
};
class B
{
explicit B(int) { }
explicit B(int, int) { }
};
And in the main function :
int main()
{
A a1 = 1; // OK: copy-initialization selects A::A(int)
A a2(2); // OK: direct-initialization selects A::A(int)
A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int)
A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
A a5 = (A)1; // OK: explicit cast performs static_cast
// B b1 = 1; // error: copy-initialization does not consider B::B(int)
B b2(2); // OK: direct-initialization selects B::B(int)
B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int)
// B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
B b5 = (B)1; // OK: explicit cast performs static_cast
}
By default, a constructor is as implicit so you have two way to initialize it :
A a1 = 1; // this is copy initialization
A a2(2); // this is direct initialization
And by defining a structure as explicit just you have one way as direct :
B b2(2); // this is direct initialization
B b5 = (B)1; // not problem if you either use of assign to initialize and cast it as static_cast
Of note:
[12.2/1] Temporaries of class type are created in various contexts: ... and in some initializations (8.5).
I.e., for copy-initialization.
[12.8/15] When certain criteria are met, an implementation is allowed to omit the copy construction of a class object ...
In other words, a good compiler will not create a copy for copy-initialization when it can be avoided; instead it will just call the constructor directly -- ie, just like for direct-initialization.
In other words, copy-initialization is just like direct-initialization in most cases <opinion> where understandable code has been written. Since direct-initialization potentially causes arbitrary (and therefore probably unknown) conversions, I prefer to always use copy-initialization when possible. (With the bonus that it actually looks like initialization.)</opinion>
Technical goriness:
[12.2/1 cont from above] Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions must be respected as if the temporary object was created.
Glad I'm not writing a C++ compiler.
First grouping: it depends on what A_factory_func returns. The first line is an example of copy initialization, the second line is direct initialization. If A_factory_func returns an A object then they are equivalent, they both call the copy constructor for A, otherwise the first version creates an rvalue of type A from an available conversion operators for the return type of A_factory_func or appropriate A constructors, and then calls the copy constructor to construct a1 from this temporary. The second version attempts to find a suitable constructor that takes whatever A_factory_func returns, or that takes something that the return value can be implicitly converted to.
Second grouping: exactly the same logic holds, except that built in types don't have any exotic constructors so they are, in practice, identical.
Third grouping: c1 is default initialized, c2 is copy-initialized from a value initialized temporary. Any members of c1 that have pod-type (or members of members, etc., etc.) may not be initialized if the user supplied default constructors (if any) do not explicitly initialize them. For c2, it depends on whether there is a user supplied copy constructor and whether that appropriately initializes those members, but the members of the temporary will all be initialized (zero-initialized if not otherwise explicitly initialized). As litb spotted, c3 is a trap. It's actually a function declaration.
Answering with respect to this part:
A c2 = A(); A c3(A());
Since most of the answers are pre-c++11 I am adding what c++11 has to say about this:
A simple-type-specifier (7.1.6.2) or typename-specifier (14.6)
followed by a parenthesized expression-list constructs a value of the
specified type given the expression list. If the expression list is a
single expression, the type conversion expression is equivalent (in
definedness, and if defined in meaning) to the corresponding cast
expression (5.4). If the type specified is a class type, the class
type shall be complete. If the expression list specifies more than a
single value, the type shall be a class with a suitably declared
constructor (8.5, 12.1), and the expression T(x1, x2, ...) is
equivalent in effect to the declaration T t(x1, x2, ...); for some
invented temporary variable t, with the result being the value of t as
a prvalue.
So optimization or not they are equivalent as per the standard.
Note that this is in accordance with what other answers have mentioned. Just quoting what the standard has to say for sake of correctness.
This is from C++ Programming Language by Bjarne Stroustrup:
An initialization with an = is considered a copy initialization. In principle, a copy of the initializer (the object we are copying from) is placed into the initialized object. However, such a copy may be optimized away (elided), and a move operation (based on move semantics) may be used if the initializer is an rvalue. Leaving out the = makes the initialization explicit. Explicit initialization is known as direct initialization.
A lot of these cases are subject to an object's implementation so it's hard to give you a concrete answer.
Consider the case
A a = 5;
A a(5);
In this case assuming a proper assignment operator & initializing constructor which accept a single integer argument, how I implement said methods affects the behavior of each line. It is common practice however for one of those to call the other in the implementation as to eliminate duplicate code (although in a case as simple as this there would be no real purpose.)
Edit: As mentioned in other responses, the first line will in fact call the copy constructor. Consider the comments relating to the assignment operator as behavior pertaining to a stand alone assignment.
That said, how the compiler optimizes the code will then have it's own impact. If I have the initializing constructor calling the "=" operator - if the compiler makes no optimizations, the top line would then perform 2 jumps as opposed to one in the bottom line.
Now, for the most common situations, your compiler will optimize through these cases and eliminate this type of inefficiencies. So effectively all the different situations you describe will turn out the same. If you want to see exactly what is being done, you can look at the object code or an assembly output of your compiler.
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.
C++17 Update
In C++17, the meaning of A_factory_func() changed from creating a temporary object (C++<=14) to just specifying the initialization of whatever object this expression is initialized to (loosely speaking) in C++17. These objects (called "result objects") are the variables created by a declaration (like a1), artificial objects created when the initialization ends up being discarded, or if an object is needed for reference binding (like, in A_factory_func();. In the last case, an object is artificially created, called "temporary materialization", because A_factory_func() doesn't have a variable or reference that otherwise would require an object to exist).
As examples in our case, in the case of a1 and a2 special rules say that in such declarations, the result object of a prvalue initializer of the same type as a1 is variable a1, and therefore A_factory_func() directly initializes the object a1. Any intermediary functional-style cast would not have any effect, because A_factory_func(another-prvalue) just "passes through" the result object of the outer prvalue to be also the result object of the inner prvalue.
A a1 = A_factory_func();
A a2(A_factory_func());
Depends on what type A_factory_func() returns. I assume it returns an A - then it's doing the same - except that when the copy constructor is explicit, then the first one will fail. Read 8.6/14
double b1 = 0.5;
double b2(0.5);
This is doing the same because it's a built-in type (this means not a class type here). Read 8.6/14.
A c1;
A c2 = A();
A c3(A());
This is not doing the same. The first default-initializes if A is a non-POD, and doesn't do any initialization for a POD (Read 8.6/9). The second copy initializes: Value-initializes a temporary and then copies that value into c2 (Read 5.2.3/2 and 8.6/14). This of course will require a non-explicit copy constructor (Read 8.6/14 and 12.3.1/3 and 13.3.1.3/1 ). The third creates a function declaration for a function c3 that returns an A and that takes a function pointer to a function returning a A (Read 8.2).
Delving into Initializations Direct and Copy initialization
While they look identical and are supposed to do the same, these two forms are remarkably different in certain cases. The two forms of initialization are direct and copy initialization:
T t(x);
T t = x;
There is behavior we can attribute to each of them:
Direct initialization behaves like a function call to an overloaded function: The functions, in this case, are the constructors of T (including explicit ones), and the argument is x. Overload resolution will find the best matching constructor, and when needed will do any implicit conversion required.
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 <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>
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.
Assignment is different from initialization.
Both of the following lines do initialization. A single constructor call is done:
A a1 = A_factory_func(); // calls copy constructor
A a1(A_factory_func()); // calls copy constructor
but it's not equivalent to:
A a1; // calls default constructor
a1 = A_factory_func(); // (assignment) calls operator =
I don't have a text at the moment to prove this but it's very easy to experiment:
#include <iostream>
using namespace std;
class A {
public:
A() {
cout << "default constructor" << endl;
}
A(const A& x) {
cout << "copy constructor" << endl;
}
const A& operator = (const A& x) {
cout << "operator =" << endl;
return *this;
}
};
int main() {
A a; // default constructor
A b(a); // copy constructor
A c = a; // copy constructor
c = b; // operator =
return 0;
}
double b1 = 0.5; is implicit call of constructor.
double b2(0.5); is explicit call.
Look at the following code to see the difference:
#include <iostream>
class sss {
public:
explicit sss( int )
{
std::cout << "int" << std::endl;
};
sss( double )
{
std::cout << "double" << std::endl;
};
};
int main()
{
sss ddd( 7 ); // calls int constructor
sss xxx = 7; // calls double constructor
return 0;
}
If your class has no explicit constuctors than explicit and implicit calls are identical.
You can see its difference in explicit and implicit constructor types when you initialize an object :
Classes :
class A
{
A(int) { } // converting constructor
A(int, int) { } // converting constructor (C++11)
};
class B
{
explicit B(int) { }
explicit B(int, int) { }
};
And in the main function :
int main()
{
A a1 = 1; // OK: copy-initialization selects A::A(int)
A a2(2); // OK: direct-initialization selects A::A(int)
A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int)
A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
A a5 = (A)1; // OK: explicit cast performs static_cast
// B b1 = 1; // error: copy-initialization does not consider B::B(int)
B b2(2); // OK: direct-initialization selects B::B(int)
B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int)
// B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
B b5 = (B)1; // OK: explicit cast performs static_cast
}
By default, a constructor is as implicit so you have two way to initialize it :
A a1 = 1; // this is copy initialization
A a2(2); // this is direct initialization
And by defining a structure as explicit just you have one way as direct :
B b2(2); // this is direct initialization
B b5 = (B)1; // not problem if you either use of assign to initialize and cast it as static_cast
Of note:
[12.2/1] Temporaries of class type are created in various contexts: ... and in some initializations (8.5).
I.e., for copy-initialization.
[12.8/15] When certain criteria are met, an implementation is allowed to omit the copy construction of a class object ...
In other words, a good compiler will not create a copy for copy-initialization when it can be avoided; instead it will just call the constructor directly -- ie, just like for direct-initialization.
In other words, copy-initialization is just like direct-initialization in most cases <opinion> where understandable code has been written. Since direct-initialization potentially causes arbitrary (and therefore probably unknown) conversions, I prefer to always use copy-initialization when possible. (With the bonus that it actually looks like initialization.)</opinion>
Technical goriness:
[12.2/1 cont from above] Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions must be respected as if the temporary object was created.
Glad I'm not writing a C++ compiler.
First grouping: it depends on what A_factory_func returns. The first line is an example of copy initialization, the second line is direct initialization. If A_factory_func returns an A object then they are equivalent, they both call the copy constructor for A, otherwise the first version creates an rvalue of type A from an available conversion operators for the return type of A_factory_func or appropriate A constructors, and then calls the copy constructor to construct a1 from this temporary. The second version attempts to find a suitable constructor that takes whatever A_factory_func returns, or that takes something that the return value can be implicitly converted to.
Second grouping: exactly the same logic holds, except that built in types don't have any exotic constructors so they are, in practice, identical.
Third grouping: c1 is default initialized, c2 is copy-initialized from a value initialized temporary. Any members of c1 that have pod-type (or members of members, etc., etc.) may not be initialized if the user supplied default constructors (if any) do not explicitly initialize them. For c2, it depends on whether there is a user supplied copy constructor and whether that appropriately initializes those members, but the members of the temporary will all be initialized (zero-initialized if not otherwise explicitly initialized). As litb spotted, c3 is a trap. It's actually a function declaration.
Answering with respect to this part:
A c2 = A(); A c3(A());
Since most of the answers are pre-c++11 I am adding what c++11 has to say about this:
A simple-type-specifier (7.1.6.2) or typename-specifier (14.6)
followed by a parenthesized expression-list constructs a value of the
specified type given the expression list. If the expression list is a
single expression, the type conversion expression is equivalent (in
definedness, and if defined in meaning) to the corresponding cast
expression (5.4). If the type specified is a class type, the class
type shall be complete. If the expression list specifies more than a
single value, the type shall be a class with a suitably declared
constructor (8.5, 12.1), and the expression T(x1, x2, ...) is
equivalent in effect to the declaration T t(x1, x2, ...); for some
invented temporary variable t, with the result being the value of t as
a prvalue.
So optimization or not they are equivalent as per the standard.
Note that this is in accordance with what other answers have mentioned. Just quoting what the standard has to say for sake of correctness.
This is from C++ Programming Language by Bjarne Stroustrup:
An initialization with an = is considered a copy initialization. In principle, a copy of the initializer (the object we are copying from) is placed into the initialized object. However, such a copy may be optimized away (elided), and a move operation (based on move semantics) may be used if the initializer is an rvalue. Leaving out the = makes the initialization explicit. Explicit initialization is known as direct initialization.
A lot of these cases are subject to an object's implementation so it's hard to give you a concrete answer.
Consider the case
A a = 5;
A a(5);
In this case assuming a proper assignment operator & initializing constructor which accept a single integer argument, how I implement said methods affects the behavior of each line. It is common practice however for one of those to call the other in the implementation as to eliminate duplicate code (although in a case as simple as this there would be no real purpose.)
Edit: As mentioned in other responses, the first line will in fact call the copy constructor. Consider the comments relating to the assignment operator as behavior pertaining to a stand alone assignment.
That said, how the compiler optimizes the code will then have it's own impact. If I have the initializing constructor calling the "=" operator - if the compiler makes no optimizations, the top line would then perform 2 jumps as opposed to one in the bottom line.
Now, for the most common situations, your compiler will optimize through these cases and eliminate this type of inefficiencies. So effectively all the different situations you describe will turn out the same. If you want to see exactly what is being done, you can look at the object code or an assembly output of your compiler.
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.
C++17 Update
In C++17, the meaning of A_factory_func() changed from creating a temporary object (C++<=14) to just specifying the initialization of whatever object this expression is initialized to (loosely speaking) in C++17. These objects (called "result objects") are the variables created by a declaration (like a1), artificial objects created when the initialization ends up being discarded, or if an object is needed for reference binding (like, in A_factory_func();. In the last case, an object is artificially created, called "temporary materialization", because A_factory_func() doesn't have a variable or reference that otherwise would require an object to exist).
As examples in our case, in the case of a1 and a2 special rules say that in such declarations, the result object of a prvalue initializer of the same type as a1 is variable a1, and therefore A_factory_func() directly initializes the object a1. Any intermediary functional-style cast would not have any effect, because A_factory_func(another-prvalue) just "passes through" the result object of the outer prvalue to be also the result object of the inner prvalue.
A a1 = A_factory_func();
A a2(A_factory_func());
Depends on what type A_factory_func() returns. I assume it returns an A - then it's doing the same - except that when the copy constructor is explicit, then the first one will fail. Read 8.6/14
double b1 = 0.5;
double b2(0.5);
This is doing the same because it's a built-in type (this means not a class type here). Read 8.6/14.
A c1;
A c2 = A();
A c3(A());
This is not doing the same. The first default-initializes if A is a non-POD, and doesn't do any initialization for a POD (Read 8.6/9). The second copy initializes: Value-initializes a temporary and then copies that value into c2 (Read 5.2.3/2 and 8.6/14). This of course will require a non-explicit copy constructor (Read 8.6/14 and 12.3.1/3 and 13.3.1.3/1 ). The third creates a function declaration for a function c3 that returns an A and that takes a function pointer to a function returning a A (Read 8.2).
Delving into Initializations Direct and Copy initialization
While they look identical and are supposed to do the same, these two forms are remarkably different in certain cases. The two forms of initialization are direct and copy initialization:
T t(x);
T t = x;
There is behavior we can attribute to each of them:
Direct initialization behaves like a function call to an overloaded function: The functions, in this case, are the constructors of T (including explicit ones), and the argument is x. Overload resolution will find the best matching constructor, and when needed will do any implicit conversion required.
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 <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>
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.
Assignment is different from initialization.
Both of the following lines do initialization. A single constructor call is done:
A a1 = A_factory_func(); // calls copy constructor
A a1(A_factory_func()); // calls copy constructor
but it's not equivalent to:
A a1; // calls default constructor
a1 = A_factory_func(); // (assignment) calls operator =
I don't have a text at the moment to prove this but it's very easy to experiment:
#include <iostream>
using namespace std;
class A {
public:
A() {
cout << "default constructor" << endl;
}
A(const A& x) {
cout << "copy constructor" << endl;
}
const A& operator = (const A& x) {
cout << "operator =" << endl;
return *this;
}
};
int main() {
A a; // default constructor
A b(a); // copy constructor
A c = a; // copy constructor
c = b; // operator =
return 0;
}
double b1 = 0.5; is implicit call of constructor.
double b2(0.5); is explicit call.
Look at the following code to see the difference:
#include <iostream>
class sss {
public:
explicit sss( int )
{
std::cout << "int" << std::endl;
};
sss( double )
{
std::cout << "double" << std::endl;
};
};
int main()
{
sss ddd( 7 ); // calls int constructor
sss xxx = 7; // calls double constructor
return 0;
}
If your class has no explicit constuctors than explicit and implicit calls are identical.
You can see its difference in explicit and implicit constructor types when you initialize an object :
Classes :
class A
{
A(int) { } // converting constructor
A(int, int) { } // converting constructor (C++11)
};
class B
{
explicit B(int) { }
explicit B(int, int) { }
};
And in the main function :
int main()
{
A a1 = 1; // OK: copy-initialization selects A::A(int)
A a2(2); // OK: direct-initialization selects A::A(int)
A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int)
A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
A a5 = (A)1; // OK: explicit cast performs static_cast
// B b1 = 1; // error: copy-initialization does not consider B::B(int)
B b2(2); // OK: direct-initialization selects B::B(int)
B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int)
// B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
B b5 = (B)1; // OK: explicit cast performs static_cast
}
By default, a constructor is as implicit so you have two way to initialize it :
A a1 = 1; // this is copy initialization
A a2(2); // this is direct initialization
And by defining a structure as explicit just you have one way as direct :
B b2(2); // this is direct initialization
B b5 = (B)1; // not problem if you either use of assign to initialize and cast it as static_cast
Of note:
[12.2/1] Temporaries of class type are created in various contexts: ... and in some initializations (8.5).
I.e., for copy-initialization.
[12.8/15] When certain criteria are met, an implementation is allowed to omit the copy construction of a class object ...
In other words, a good compiler will not create a copy for copy-initialization when it can be avoided; instead it will just call the constructor directly -- ie, just like for direct-initialization.
In other words, copy-initialization is just like direct-initialization in most cases <opinion> where understandable code has been written. Since direct-initialization potentially causes arbitrary (and therefore probably unknown) conversions, I prefer to always use copy-initialization when possible. (With the bonus that it actually looks like initialization.)</opinion>
Technical goriness:
[12.2/1 cont from above] Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions must be respected as if the temporary object was created.
Glad I'm not writing a C++ compiler.
First grouping: it depends on what A_factory_func returns. The first line is an example of copy initialization, the second line is direct initialization. If A_factory_func returns an A object then they are equivalent, they both call the copy constructor for A, otherwise the first version creates an rvalue of type A from an available conversion operators for the return type of A_factory_func or appropriate A constructors, and then calls the copy constructor to construct a1 from this temporary. The second version attempts to find a suitable constructor that takes whatever A_factory_func returns, or that takes something that the return value can be implicitly converted to.
Second grouping: exactly the same logic holds, except that built in types don't have any exotic constructors so they are, in practice, identical.
Third grouping: c1 is default initialized, c2 is copy-initialized from a value initialized temporary. Any members of c1 that have pod-type (or members of members, etc., etc.) may not be initialized if the user supplied default constructors (if any) do not explicitly initialize them. For c2, it depends on whether there is a user supplied copy constructor and whether that appropriately initializes those members, but the members of the temporary will all be initialized (zero-initialized if not otherwise explicitly initialized). As litb spotted, c3 is a trap. It's actually a function declaration.
Answering with respect to this part:
A c2 = A(); A c3(A());
Since most of the answers are pre-c++11 I am adding what c++11 has to say about this:
A simple-type-specifier (7.1.6.2) or typename-specifier (14.6)
followed by a parenthesized expression-list constructs a value of the
specified type given the expression list. If the expression list is a
single expression, the type conversion expression is equivalent (in
definedness, and if defined in meaning) to the corresponding cast
expression (5.4). If the type specified is a class type, the class
type shall be complete. If the expression list specifies more than a
single value, the type shall be a class with a suitably declared
constructor (8.5, 12.1), and the expression T(x1, x2, ...) is
equivalent in effect to the declaration T t(x1, x2, ...); for some
invented temporary variable t, with the result being the value of t as
a prvalue.
So optimization or not they are equivalent as per the standard.
Note that this is in accordance with what other answers have mentioned. Just quoting what the standard has to say for sake of correctness.
This is from C++ Programming Language by Bjarne Stroustrup:
An initialization with an = is considered a copy initialization. In principle, a copy of the initializer (the object we are copying from) is placed into the initialized object. However, such a copy may be optimized away (elided), and a move operation (based on move semantics) may be used if the initializer is an rvalue. Leaving out the = makes the initialization explicit. Explicit initialization is known as direct initialization.
A lot of these cases are subject to an object's implementation so it's hard to give you a concrete answer.
Consider the case
A a = 5;
A a(5);
In this case assuming a proper assignment operator & initializing constructor which accept a single integer argument, how I implement said methods affects the behavior of each line. It is common practice however for one of those to call the other in the implementation as to eliminate duplicate code (although in a case as simple as this there would be no real purpose.)
Edit: As mentioned in other responses, the first line will in fact call the copy constructor. Consider the comments relating to the assignment operator as behavior pertaining to a stand alone assignment.
That said, how the compiler optimizes the code will then have it's own impact. If I have the initializing constructor calling the "=" operator - if the compiler makes no optimizations, the top line would then perform 2 jumps as opposed to one in the bottom line.
Now, for the most common situations, your compiler will optimize through these cases and eliminate this type of inefficiencies. So effectively all the different situations you describe will turn out the same. If you want to see exactly what is being done, you can look at the object code or an assembly output of your compiler.
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.
C++17 Update
In C++17, the meaning of A_factory_func() changed from creating a temporary object (C++<=14) to just specifying the initialization of whatever object this expression is initialized to (loosely speaking) in C++17. These objects (called "result objects") are the variables created by a declaration (like a1), artificial objects created when the initialization ends up being discarded, or if an object is needed for reference binding (like, in A_factory_func();. In the last case, an object is artificially created, called "temporary materialization", because A_factory_func() doesn't have a variable or reference that otherwise would require an object to exist).
As examples in our case, in the case of a1 and a2 special rules say that in such declarations, the result object of a prvalue initializer of the same type as a1 is variable a1, and therefore A_factory_func() directly initializes the object a1. Any intermediary functional-style cast would not have any effect, because A_factory_func(another-prvalue) just "passes through" the result object of the outer prvalue to be also the result object of the inner prvalue.
A a1 = A_factory_func();
A a2(A_factory_func());
Depends on what type A_factory_func() returns. I assume it returns an A - then it's doing the same - except that when the copy constructor is explicit, then the first one will fail. Read 8.6/14
double b1 = 0.5;
double b2(0.5);
This is doing the same because it's a built-in type (this means not a class type here). Read 8.6/14.
A c1;
A c2 = A();
A c3(A());
This is not doing the same. The first default-initializes if A is a non-POD, and doesn't do any initialization for a POD (Read 8.6/9). The second copy initializes: Value-initializes a temporary and then copies that value into c2 (Read 5.2.3/2 and 8.6/14). This of course will require a non-explicit copy constructor (Read 8.6/14 and 12.3.1/3 and 13.3.1.3/1 ). The third creates a function declaration for a function c3 that returns an A and that takes a function pointer to a function returning a A (Read 8.2).
Delving into Initializations Direct and Copy initialization
While they look identical and are supposed to do the same, these two forms are remarkably different in certain cases. The two forms of initialization are direct and copy initialization:
T t(x);
T t = x;
There is behavior we can attribute to each of them:
Direct initialization behaves like a function call to an overloaded function: The functions, in this case, are the constructors of T (including explicit ones), and the argument is x. Overload resolution will find the best matching constructor, and when needed will do any implicit conversion required.
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 <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>
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.
Assignment is different from initialization.
Both of the following lines do initialization. A single constructor call is done:
A a1 = A_factory_func(); // calls copy constructor
A a1(A_factory_func()); // calls copy constructor
but it's not equivalent to:
A a1; // calls default constructor
a1 = A_factory_func(); // (assignment) calls operator =
I don't have a text at the moment to prove this but it's very easy to experiment:
#include <iostream>
using namespace std;
class A {
public:
A() {
cout << "default constructor" << endl;
}
A(const A& x) {
cout << "copy constructor" << endl;
}
const A& operator = (const A& x) {
cout << "operator =" << endl;
return *this;
}
};
int main() {
A a; // default constructor
A b(a); // copy constructor
A c = a; // copy constructor
c = b; // operator =
return 0;
}
double b1 = 0.5; is implicit call of constructor.
double b2(0.5); is explicit call.
Look at the following code to see the difference:
#include <iostream>
class sss {
public:
explicit sss( int )
{
std::cout << "int" << std::endl;
};
sss( double )
{
std::cout << "double" << std::endl;
};
};
int main()
{
sss ddd( 7 ); // calls int constructor
sss xxx = 7; // calls double constructor
return 0;
}
If your class has no explicit constuctors than explicit and implicit calls are identical.
You can see its difference in explicit and implicit constructor types when you initialize an object :
Classes :
class A
{
A(int) { } // converting constructor
A(int, int) { } // converting constructor (C++11)
};
class B
{
explicit B(int) { }
explicit B(int, int) { }
};
And in the main function :
int main()
{
A a1 = 1; // OK: copy-initialization selects A::A(int)
A a2(2); // OK: direct-initialization selects A::A(int)
A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int)
A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
A a5 = (A)1; // OK: explicit cast performs static_cast
// B b1 = 1; // error: copy-initialization does not consider B::B(int)
B b2(2); // OK: direct-initialization selects B::B(int)
B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int)
// B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
B b5 = (B)1; // OK: explicit cast performs static_cast
}
By default, a constructor is as implicit so you have two way to initialize it :
A a1 = 1; // this is copy initialization
A a2(2); // this is direct initialization
And by defining a structure as explicit just you have one way as direct :
B b2(2); // this is direct initialization
B b5 = (B)1; // not problem if you either use of assign to initialize and cast it as static_cast
Of note:
[12.2/1] Temporaries of class type are created in various contexts: ... and in some initializations (8.5).
I.e., for copy-initialization.
[12.8/15] When certain criteria are met, an implementation is allowed to omit the copy construction of a class object ...
In other words, a good compiler will not create a copy for copy-initialization when it can be avoided; instead it will just call the constructor directly -- ie, just like for direct-initialization.
In other words, copy-initialization is just like direct-initialization in most cases <opinion> where understandable code has been written. Since direct-initialization potentially causes arbitrary (and therefore probably unknown) conversions, I prefer to always use copy-initialization when possible. (With the bonus that it actually looks like initialization.)</opinion>
Technical goriness:
[12.2/1 cont from above] Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions must be respected as if the temporary object was created.
Glad I'm not writing a C++ compiler.
First grouping: it depends on what A_factory_func returns. The first line is an example of copy initialization, the second line is direct initialization. If A_factory_func returns an A object then they are equivalent, they both call the copy constructor for A, otherwise the first version creates an rvalue of type A from an available conversion operators for the return type of A_factory_func or appropriate A constructors, and then calls the copy constructor to construct a1 from this temporary. The second version attempts to find a suitable constructor that takes whatever A_factory_func returns, or that takes something that the return value can be implicitly converted to.
Second grouping: exactly the same logic holds, except that built in types don't have any exotic constructors so they are, in practice, identical.
Third grouping: c1 is default initialized, c2 is copy-initialized from a value initialized temporary. Any members of c1 that have pod-type (or members of members, etc., etc.) may not be initialized if the user supplied default constructors (if any) do not explicitly initialize them. For c2, it depends on whether there is a user supplied copy constructor and whether that appropriately initializes those members, but the members of the temporary will all be initialized (zero-initialized if not otherwise explicitly initialized). As litb spotted, c3 is a trap. It's actually a function declaration.
Answering with respect to this part:
A c2 = A(); A c3(A());
Since most of the answers are pre-c++11 I am adding what c++11 has to say about this:
A simple-type-specifier (7.1.6.2) or typename-specifier (14.6)
followed by a parenthesized expression-list constructs a value of the
specified type given the expression list. If the expression list is a
single expression, the type conversion expression is equivalent (in
definedness, and if defined in meaning) to the corresponding cast
expression (5.4). If the type specified is a class type, the class
type shall be complete. If the expression list specifies more than a
single value, the type shall be a class with a suitably declared
constructor (8.5, 12.1), and the expression T(x1, x2, ...) is
equivalent in effect to the declaration T t(x1, x2, ...); for some
invented temporary variable t, with the result being the value of t as
a prvalue.
So optimization or not they are equivalent as per the standard.
Note that this is in accordance with what other answers have mentioned. Just quoting what the standard has to say for sake of correctness.
This is from C++ Programming Language by Bjarne Stroustrup:
An initialization with an = is considered a copy initialization. In principle, a copy of the initializer (the object we are copying from) is placed into the initialized object. However, such a copy may be optimized away (elided), and a move operation (based on move semantics) may be used if the initializer is an rvalue. Leaving out the = makes the initialization explicit. Explicit initialization is known as direct initialization.
A lot of these cases are subject to an object's implementation so it's hard to give you a concrete answer.
Consider the case
A a = 5;
A a(5);
In this case assuming a proper assignment operator & initializing constructor which accept a single integer argument, how I implement said methods affects the behavior of each line. It is common practice however for one of those to call the other in the implementation as to eliminate duplicate code (although in a case as simple as this there would be no real purpose.)
Edit: As mentioned in other responses, the first line will in fact call the copy constructor. Consider the comments relating to the assignment operator as behavior pertaining to a stand alone assignment.
That said, how the compiler optimizes the code will then have it's own impact. If I have the initializing constructor calling the "=" operator - if the compiler makes no optimizations, the top line would then perform 2 jumps as opposed to one in the bottom line.
Now, for the most common situations, your compiler will optimize through these cases and eliminate this type of inefficiencies. So effectively all the different situations you describe will turn out the same. If you want to see exactly what is being done, you can look at the object code or an assembly output of your compiler.
I'm reading a tutorial for C++ but it didn't actually give me a difference (besides syntax) between the two. Here is a quote from the tutorial.
You can also assign values to your variables upon declaration. When we
assign values to a variable using the assignment operator (equals
sign), it’s called an explicit assignment:
int nValue = 5; // explicit assignment
You can also assign values to variables using an implicit assignment:
int nValue(5); // implicit assignment
Even though implicit assignments look a lot like function calls, the
compiler keeps track of which names are variables and which are
functions so that they can be resolved properly.
Is there a difference? Is one more preferred over the other?
The first is preferred with primitive types like int; the second with types that have a constructor, because it makes the constructor call explicit.
E.g., if you've defined a class Foo that can be constructed from a single int, then
Foo x(5);
is preferred over
Foo x = 5;
(You need the former syntax anyway when more than one argument is passed, unless you use Foo x = Foo(5, "hello"); which is plain ugly and looks like operator= is being called.)
For primitive types both are equivalent, it is for user defined class types that there is a difference. In both cases the code that gets executed will be the same (after basic optimizations are performed), but the requirements on the types differ if the element from which we are initializing is not of the type that we are constructing.
The copy-initialization (T t = u;) is equivalent to copy construction from a temporary of type T that has been implicitly converted from u to t. On the other hand direct-initialization is equivalent to a direct call to the appropriate constructor.
While in most circumstances there will be no difference, if the constructor that takes the u is declared explicit or if the copy-constructor is inaccessible, then copy-initialization will fail:
struct A {
explicit A( int ) {}
};
struct B {
B( int ) {}
private:
B( B const & );
};
int main() {
A a(1); // ok
B b(1); // ok
// A a2 = 1; // error: cannot convert from int to A
// B b2 = 1; // error: B( B const & ) is not accessible
}
For some historical background, initially primitive types had to be initialized with copy-initialization. When *initializer-list*s were added to the language to initialize member attributes from a class, it was decided that primitive types should be initialized with the same syntax that classes to keep the syntax in the initializer list uniform and simple. At the same time allowing the initialization of classes by means of copy-initialization makes user defined types closer to primitive types. The differences in the two initialization formats comes naturally: int a = 5.0; is processed as a conversion from 5.0 to int, and then initialization of a from the int. The same goes with user defined types: T u = v; is processed as conversion from v to T, and then copy construction of u from that converted value.
when you are declaring a variable and initializing it, they are functionally the same in that context. I usually refer to the two as:
int nValue = 5; // assignment syntax
and
int nValue(5); // construction syntax
For basic types, I prefer assignment over construction since it is more natural, especially for those who have programmed in other languages.
For class types, I prefer construction syntax since it obviates the existence of a constructor function.