This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115e. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2024-11-11
[Accepted as a DR at the March, 2024 meeting.]
(From submission #486.)
Consider:
struct A { explicit A(int = 10); A() = default; // converting constructor (11.4.8.2 [class.conv.ctor] paragraph 1) }; A a = {}; // #1, copy-initialization int f(A); int x = f({}); // #2 A b; // #3
#1 and #2 are accepted by MSVC and EDG, but considered ambiguous by gcc and clang. #3 is rejected as ambiguous by all major implementations.
#1 is copy-list-initialization (9.4.5 [dcl.init.list] paragraph 1), and A has a default constructor, thus a is value-initialized (9.4.5 [dcl.init.list] bullet 3.4). The default constructors are user-provided, thus a is default-initialized (9.4.1 [dcl.init.general] bullet 8.1, 9.4.1 [dcl.init.general] bullet 7.1). Overload resolution then chooses a constructor according to 12.2.2.4 [over.match.ctor] paragraph 1:
... For copy-initialization (including default initialization in the context of copy-initialization), the candidate functions are all the converting constructors (11.4.8.2 [class.conv.ctor]) of that class. ...
Thus, the explicit constructor is not a candidate; #1 chooses the converting constructor.
In contrast, #2 uses the special rules for forming a list-initialization sequence (12.2.4.2.6 [over.ics.list] paragraph 7), which perform overload resolution according to 12.2.2.8 [over.match.list] paragraph 1, which considers all constructors for overload resolution (and makes the program ill-formed if an explicit constructor is chosen). For the example, overload resolution is ambiguous.
#3 performs default-initialization, and overload resolution then chooses a constructor according to 12.2.2.4 [over.match.ctor] paragraph 1:
... For direct-initialization or default-initialization that is not in the context of copy-initialization, the candidate functions are all the constructors of the class of the object being initialized. ...
In this case, the overload resolution is ambiguous.
Proposed resolution (approved by CWG 2024-02-16):
Change in 12.2.2.4 [over.match.ctor] paragraph 1 as follows:
When objects of class type are direct-initialized (9.4 [dcl.init]), copy-initialized from an expression of the same or a derived class type (9.4 [dcl.init]), or default-initialized (9.4 [dcl.init]), overload resolution selects the constructor. For direct-initialization or default-initializationthat is not in the context of copy-initialization(including default-initialization in the context of copy-list-initialization), the candidate functions are all the constructors of the class of the object being initialized.For copy-initialization (including default initialization in the context of copy-initialization)Otherwise, the candidate functions are all the converting constructors (11.4.8.2 [class.conv.ctor]) of that class. The argument list is the expression-list or assignment-expression of the initializer. For default-initialization in the context of copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.
CWG 2024-02-16
The fact that #2 considers all constructors was discussed (and established) in the C++11 timeframe when brace-initialization was first introduced. #1 should be consistent with that, even though the = is usually a clear sign that explicit constructors are not considered.