I've recently read somewhere (can't remember where) about using braces to allow multiple user-defined conversions, but there seems to be a difference between conversion by constructor and conversion by conversion method that I don't understand.
Consider:
#include <string>
using ::std::string;
struct C {
C() {}
};
struct A {
A(const string& s) {} // Make std::string convertible to A.
operator C() const { return C(); } // Makes A convertible to C.
};
struct B {
B() {}
B(const A& a) {} // Makes A convertible to B.
};
int main() {
B b;
C c;
// This works.
// Conversion chain (all thru ctors): char* -> string -> A -> B
b = {{"char *"}};
// These two attempts to make the final conversion through A's
// conversion method yield compiler errors.
c = {{"char *"}};
c = {{{"char *"}}};
// On the other hand, this does work (not surprisingly).
c = A{"char *"};
}
Now, I may be misinterpreting what the compiler is doing, but (based on the above and additional experimentation) it seems to me that it's not considering conversions by conversion-method. Reading through Sections 4 and 13.3.3.1 of the standard, however, I wasn't able to find a clue why this is. What is the explanation?
Update
Here's another interesting phenomenon I'd like explained. If I add
struct D {
void operator<<(const B& b) {}
};
and in main:
D d;
d << {{ "char *" }};
I get an error, but if instead I write d.operator<<({{ "char *" }}); it works fine.
Update 2
Looks like Section 8.5.4 in the standard may hold some answers. I'll report my findings.
There is one user conversion possible.
In b = {{"char *"}};
we actually do
b = B{{"char*"}}; // B has constructor with A (and a copy constructor not viable here)
so
b = B{A{"char*"}}; // One implicit conversion const char* -> std::string
in c = {{"const char*"}}, we try
c = C{{"char *"}}; // but nothing to construct here.
Digging through Section 8.5.4 of the standard and following various cross references therein, I think I understand what's going on. Of course, IANAL, so I might be wrong; this is my best effort.
Update: The previous version of the answer actually used multiple conversions. I've updated it to reflect my current understanding.
The key to unraveling the mess is the fact that a braced-init-list is not an expression (which also explains why d << {{"char *"}} won't compile). What it is is special syntax, governed by special rules, that is allowed in a number of specific contexts. Of these contexts, the relevant ones for our discussion are: rhs of assignment, argument in a function call, and argument in a constructor invocation.
So what happens when the compiler sees b = {{"char *"}}? This is a case of rhs of assignment. The applicable rule is:
A braced-init-list may appear on the right-hand side of ... an assignment defined by a user-defined assignment operator, in which case the initializer list is passed
as the argument to the operator function.
(Presumably, the default copy assignment operator is considered a user-defined assignment operator. I couldn't find a definition of that term anywhere, and there doesn't seem to be any language allowing the brace syntax specifically for default copy assignment.)
So we are reduced to argument passing to the default copy assignment operator B::operator=(const B&), where the argument being passed is {{"char *"}}. Because a braced-init-list is not an expression, there is no issue of conversion here, but rather a form of initialization of a temporary of type B, specifically, so called list initialization.
If no viable initializer-list constructor is found, overload resolution is performed again, where the
candidate functions are all the constructors of the class T and the argument list consists of the elements
of the initializer list.
So the compiler strips off the outer pair of braces and performs overload resolution using {"char *"} as the argument. This succeeds, matching the constructor B::B(const A&) because there is again list initialization of a temporary of type A in which overload resolution succeeds to match A::A(const string&) for the argument "char *", which is possible through the one alloted user-defined conversion, namely, from char* to string.
Now, in the case of c = {{"char *"}} the process is similar, but when we try to list-initialize a temporary of type C with {{"char *"}}, overload resolution fails to find a constructor that matches. The point is that by definition list-initialization only works through a constructor whose parameter list can be made to match the contents of the list.
Related
I was trying to overload the casting operator in C++ for practice, but I encountered a problem and I can't figure out the issue. In the example, you can implicitly cast fine, but it causes an error when you try to explicitly cast.
struct B
{
B() = default;
B( B& rhs ) = default;
};
struct A
{
operator B()
{
return B();
}
};
int main()
{
A a;
B example = a; //fine
B example2 = static_cast<B>(a); //error
}
The error is:
error C2440: 'static_cast': cannot convert from 'A' to 'B'
message : No constructor could take the source type, or constructor overload resolution was ambiguous
The problem only appears if you define the copy constructor in the B structure. The problem goes away, though, if you define the move constructor, too, or make the copy constructor take in a const B& ( B( const B& rhs ) ).
I think the problem is that the explicit cast is ambiguous, but I don't see how.
I was looking at a similar problem, here, but in that case I could easily see how the multiple options for casting led to ambiguity while I can't here.
static_cast<B>(a);
This expression is an rvalue, more loosely described as a temporary value.
The B class has no suitable constructor. The B( B &rhs) constructor is not suitable, mutable lvalue references don't bind to temporaries, hence the compilation failure.
Before C++17:
Both lines do effectively the same thing. B example = /*...*/; is copy-initialization which will first convert the right-hand side to the type B if necessary in some suitable manner, resulting in a temporary object from which example is then initialized by choosing a suitable constructor (typically a copy or move constructor).
Because A is not related to B via inheritance, there is no way to bind a directly to the rhs parameter in the (copy) constructor of B. There must first be a conversion from a to a B which is possible implicitly or explicitly via the conversion function you defined.
The result of the conversion will always be a prvalue. A prvalue however can not be bound to a non-const lvalue reference. Therefore it doesn't help that the conversion is possible, the B( B& rhs ) constructor overload is still not viable.
A constructor B( const B& rhs ) (which is also the signature of the implicitly-declared copy constructor if you don't declare one yourself) would however be viable because the prvalue resulting from the conversion can be bound to a const lvalue reference.
Therefore both lines are ill-formed, before C++17.
Since C++17 the mandatory copy elision rules state that initialization of a variable of a type B from a prvalue of type B is done exactly as if the variable was directly initialized by the initializer of the prvalue, so that no overload resolution on the constructor will be performed at all. Whether there is a copy constructor or how exactly it is declared is then irrelevant. Similarly the rules for copy-initialization from a non-reference compatible lvalue such as a have been updated to have the same effect.
So since C++17 both lines will compile.
If you are using MSVC in a default configuration with the language standard set below C++17, the first line will compile, but that is not standard-conforming. By default when set to language versions below C++20 MSVC uses a slight dialect of C++ which is not conforming to the standard in some areas. You can set the standards conformance mode (/permissive- flag) to set MSVC to behave (more) standard-conforming.
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.
In bullet point (17.6.2) in §8.5[dcl.init]/17 in N4140 we have (emphasis is mine):
Otherwise (i.e., for the remaining copy-initialization cases),
user-defined conversion sequences that can convert from the source
type to the destination type or (when a conversion function is used)
to a derived class thereof are enumerated as described in 13.3.1.4,
and the best one is chosen through overload resolution (13.3). If the
conversion cannot be done or is ambiguous, the initialization is
ill-formed. The function selected is called with the initializer
expression as its argument; if the function is a constructor, the call
initializes a temporary of the cv-unqualified version of the
destination type. The temporary is a prvalue. The result of the call
(which is the temporary for the constructor case) is then used to
direct-initialize, according to the rules above, the object that is
the destination of the copy-initialization. In certain cases, an
implementation is permitted to eliminate the copying inherent in this
direct-initialization by constructing the intermediate result directly
into the object being initialized; see 12.2, 12.8.
The portion of the text in bold seems to indicate that direct-initializations will never invoke a user-defined conversion sequence. But that's not what I found out below:
#include <iostream>
struct A {
A() { std::cout << "default ctor A" << '\n'; }
A(const A&) { std::cout << "copy A" << '\n'; }
};
struct C {
C() { std::cout << "default ctor C" << '\n'; }
operator A() { std::cout << "C::operator A()" << '\n'; return A(); };
};
int main()
{
C c;
A a{c}; // direct-initialization where the C::operator A() is invoked
// to copy construct the object `a`.
}
The following is printed by this snippet:
default ctor C
C::operator A()
default ctor A
copy A
copy A
See live example
Edit
In response to #Johannes answer, please consider A a(c); instead of A a{c};. The rest remains valid, as far as I can understand.
When discussing direct initialization (emphasis is my own):
If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified
version of the source type is the same class as, or a derived class of, the class of the destination,
constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best
one is chosen through overload resolution (13.3). The constructor so selected is called to initialize
the object, with the initializer expression or expression-list as its argument(s). If no constructor
applies, or the overload resolution is ambiguous, the initialization is ill-formed.
This line leads to section §8.5 (2.8,2.9) on Overload Resolutions which describes the process.
Overload resolution selects the function to call in seven distinct contexts within the language:
...
(2.4) — invocation of a constructor for direct-initialization (8.5) of a class object...
But, once the candidate functions and argument lists have been identified, the selection of the best function
is the same in all cases:
(2.8) — First, a subset of the candidate functions (those that have the proper number of arguments and meet
certain other conditions) is selected to form a set of viable functions (13.3.2).
(2.9) — Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed
to match each argument to the corresponding parameter of each viable function.
Given the above, direct initialization does in fact go through the process of selecting candidate constructors (in your case, the one parameter constructor A::A( A const &) ) and finding an implicit converse sequence to match the two together - that is operator C::A().
It is somewhat misleading that they apply - what seems to be - redundant language in the text for other copy-initialization cases.
The extra language on an implicit conversion sequence - that you quote in the question - for the other copy-initialization cases of (17.6.2) directly refer to the following case:
class B {
operator A();
};
class A {
};
B b;
A a = b;
This isn't covered under the first point in (17.6.1) as B doesn't derive from A nor do any helpful constructors exist. Therefore it falls under the 'other' clause.
I don't understand your concern, but answering what happens, in case this can solve it for you. A a{c} directly branches off of bullet 1 of dcl.initp17 to dcl.init.list.
From there it happens that it will initialize a const A& from a C (by over.match.list selecting the copy-constructor of A). This will invoke the paragraph you cited (by first branching off to dcl.init.ref and then applying dcl.initp17 in turn). This will construct a normal user-defined conversion sequence which is able to use conversion functions, such as your operator A.
I think you are confused by the fact that it only mentions user defined conversion sequences for the copy initializations, but in the preceeding bullet it doesn't. The reason is that when doing direct initialization, that is conceptual a direct call to the constructors of a class instead of being a conversion (in which the call to constructors is only a part, surrounded by a standard conversion sequence before (for constructors) and after (for conversion functions/operators) it). In other words, for direct initialization, you already force the constructor call, rather than making the compiler find out that it needs to do it. And you haven't "paid" a user defined conversion sequence (you cannot nest user defined conversion sequences, so they are "precious") - the compiler can still apply a user defined conversion sequence for matching up the argument for the constructor.
The proper testcase for this would be A a(c) rather than A a{c} though, to prevent branching off to dcl.init.list.
I am just looking for clarification on how C++ works, this isn't really about solving a particular problem in my code.
In C++ you can say that type A should implicitly convert to type B in two different ways.
If you are the author of A you can add something like this to A:
operator B() {
// code
}
If you are the author of B you can add something like this to B:
B(const A &a) {
// code
}
Either one of these, if I understand correctly, will allow A to implicitly convert to B. So if both are defined which one is used? Is that even allowed?
NOTE: I understand that you should probably never be in a situation where you do this. You would either make the constructor explicit or even more likely only have one of the two. I am just wondering what the C++ specification says and I don't know how to look that up.
Unfortunately, the answer to this question is probably more complex than what you were looking for. It is true that the compiler will reject ambiguous conversions as Lightness Races in Orbit points out, but are the conversions ambiguous? Let's examine a few cases. All references are to the C++11 standard.
Explicit conversion
This doesn't address your question directly because you asked about implicit conversion, but since Lightness Races in Orbit gave an example of explicit conversion, I'll go over it anyway.
Explicit conversion is performed from A to B when:
you use the syntax (B)a, where a is of type A, which in this case will be equivalent to static_cast<B>(a) (C++11 standard, §5.4/4).
you use a static cast, which in this case will create a temporary which is initialized in the same way that the declaration B t(a); initializes t; (§5.2.9/4)
you use the syntax B(a), which is equivalent to (B)a and hence also does the same thing as the initialization in the declaration B t(a); (§5.2.3/1)
In each case, therefore, direct initialization is performed of a prvalue of type B using a value of type A as the argument. §8.5/16 specifies that only constructors are considered, so B::B(const A&) will be called. (For slightly more detail, see my answer here: https://stackoverflow.com/a/22444974/481267)
Copy-initialization
In the copy-initialization
B b = a;
the value a of type A is first converted to a temporary of type B using a user-defined conversion sequence, which is an implicit conversion sequence. Then this temporary is used to direct-initialize b.
Because this is copy-initialization of a class type by an object of a different class type, both the converting constructor B::B(const A&) and the conversion function A::operator B() are candidates for the conversion (§13.3.1.4). The latter is called because it wins overload resolution. Note that if B::B had argument A& rather than const A&, the overload would be ambiguous and the program wouldn't compile. For details and references to the Standard see this answer: https://stackoverflow.com/a/1384044/481267
Copy-list-initialization
The copy-list-initialization
B b = {a};
only considers constructors of B (§8.5.4/3), and not conversion functions of A, so B::B(const A&) will be called, just like in explicit conversion.
Implicit conversion of function arguments
If we have
void f(B b);
A a;
f(a);
then the compiler has to select the best implicit conversion sequence to convert a to type B in order to pass it to f. For this purpose, user-defined conversion sequences are considered which consist of a standard conversion followed by a user-defined conversion followed by another standard conversion (§13.3.3.1.2/1). A user-defined conversion can occur through either the converting constructor B::B(const A&) or through the conversion function A::operator B().
Here's where it gets tricky. There is some confusing wording in the standard:
Since an implicit conversion sequence is an initialization, the special rules for initialization
by user-defined conversion apply when selecting the best user-defined conversion for a user-defined conversion
sequence (see 13.3.3 and 13.3.3.1).
(§13.3.3.1.2/2)
To make a long story short, this means that the user-defined conversion in the user-defined conversion sequence from A to B is itself subject to overload resolution; A::operator B() wins over B::B(const A&) because the former has less cv-qualification (as in the copy-initialization case) and ambiguity would result if we had had B::B(A&) rather than B::B(const A&). Note that this cannot result in the infinite recursion of overload resolution, since user-defined conversions are not allowed for converting the argument to the parameter type of the user-defined conversion.
Return statement
In
B foo() {
return A();
}
the expression A() is implicitly converted to type B (§6.6.3/2) so the same rules apply as in implicit conversion of function arguments; A::operator B() will be called, and the overload would be ambiguous if we had B::B(A&). However, if it were instead
return {A()};
then this would be a copy-list-initialization instead (§6.6.3/2 again); B::B(const A&) will be called.
Note: user-defined conversions are not tried when handling exceptions; a catch(B) block won't handle a throw A();.
[C++11: 12.3/2]: User-defined conversions are applied only where they are unambiguous. [..]
12.3 goes on to list the two kinds you identified.