As of the latest official standard, C++14, your first initialization is not ambiguous. [over.match.list]:
As no initializer-list constructors exist, we enter the "second phase". And now consider [over.best.ics]/4:
Our element is {0}
. Hence this disallows the (user-defined) conversion {0}
-> A
for the copy constructor. Clearly, this doesn't apply if we aren't in the second phase of [over.match.list], so for your example with B c({0})
, no list-initialization occurs for c
and both constructors are considered.
CWG issue 1467
The first initialization is currently just as ambiguous as the second one. Compilers simply haven't implemented CWG #1467 yet - its resolution removed bullet point (4.5), quoted above.
See #2076, which opts to revert the change:
The resolution of issue 1467 made some plausible constructs
ill-formed. For example,
struct A { A(int); };
struct B { B(A); };
B b{{0}};
This is now ambiguous, because the text disallowing user-defined
conversions for B
's copy and move constructors was removed from
13.3.3.1 [over.best.ics] paragraph 4.
"The text" is the aforementioned bullet point. Richard Smith proposes the following wording:
For non-class types, we allow initialization from a single-item list
to perform a copy only if the element within the list is not itself a
list (13.3.3.1.5 [over.ics.list] bullet 9.1). The analogous rule for
this case would be to add back the bullet in 13.3.3.1 [over.best.ics]
paragraph 4, but only in the case where the initializer is itself an
initializer list:
the second phase of
13.3.1.7 [over.match.list] when the initializer list has exactly one element that is
itself an initializer list, where the target is the first parameter of
a constructor
of class X
, and the conversion is
to X
or reference to (possibly cv-qualified) X
,
As the initializer {0}
is itself an initializer list, that bullet point would make your first initialization well-formed again.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…