Multiple implicit conversions on custom types not allowed?
class C {
public:
C() { }
};
class B {
public:
B(C c) { }
B() { }
};
class A {
public:
A(bool b) { }
A(B b) { }
};
int main() {
A a1 = true; // bool -> A is allowed
A a2 = B(); // B -> A is allowed
A a3 = 7; // int -> bool -> A is allowed
A a4 = C(); // C -> B -> A isn't allowed
}
Why I can use two-step implicit conversion with bool
but can't use it with C
?
What is the general rule describing multistep implicit conversion?
There is no multi-step user-defined implicit conversion.
int -> bool -> A
is allowed because the int->bool
conversion isn't user-defined.
12.3 Conversions [class.conv]
1 Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions and are used for implicit type conversions (clause 4), for initialization (8.5), and for explicit type conversions (5.4, 5.2.9).
2 User-defined conversions are applied only where they are unambiguous (10.2, 12.3.2). Conversions obey the access control rules (clause 11). Access control is applied after ambiguity resolution (3.4).
3 [ Note: See 13.3 for a discussion of the use of conversions in function calls as well as examples below. —end note ]
4 At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
Since this construction is perfectly legal
A a4((C()));
problem is, that you use copy initization. Really, your example is equal to
A a4((A(C()));
8.5/16
The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. If the initializer is not a single (possibly parenthesized) expression, the source type is not defined.
If the destination type is a (possibly cv-qualified) class type:
— 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).
13.3.1.4/1
Under the conditions specified in 8.5, as part of a copy-initialization of an object of class type, a user-defined conversion can be invoked to convert an initializer expression to the type of the object being initialized.
Overload resolution is used to select the user-defined conversion to be invoked. Assuming that “cv1 T” is the type of the object being initialized, with T a class type, the candidate functions are selected as follows: — The converting constructors (12.3.1) of T are candidate functions.
— When the type of the initializer expression is a class type “cv S”, the non-explicit conversion functions of S and its base classes are considered.
13.3.3.1/4
However, when considering the argument of a constructor or user-defined conversion function that is a candidate by 13.3.1.3 when invoked for the copying/moving of the temporary in the second step of a class copy-initialization, by 13.3.1.7 when passing the initializer list as a single argument or when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is considered for the first parameter of a constructor of X, or by 13.3.1.4, 13.3.1.5, or 13.3.1.6 in all cases, only standard conversion sequences and ellipsis conversion sequences are considered.
Your user-defined conversion (C -> B) is not considered in this case.