According to the C++23 standard, what is the output of this program?
#include <initializer_list>
#include <iostream>
struct A {
A() { std::cout << "1"; }
A(int) { std::cout << "2"; }
A(std::initializer_list<int>) { std::cout << "3"; }
};
int main(int argc, char *argv[]) {
A a1;
A a2{};
A a3{ 1 };
A a4{ 1, 2 };
}How greedy are initializer-list constructors?
1133
a1 is default initialized, as described in §[dcl.init.general]¶12.
a2 doesn't actually use the initializer_list constructor with a list of zero elements, but the default constructor.
List-initialization of an object or reference of type
Tis defined as follows:— (...)
— Otherwise, if the initializer list has no elements and
Tis a class type with a default constructor, the object is value-initialized.
a3's and a4's constructor is chosen in overload resolution, as described in §[over.match.list]¶1:
When objects of non-aggregate class type
Tare list-initialized (...), overload resolution selects the constructor in two phases:— If the initializer list is not empty or
Thas no default constructor, overload resolution is first performed where the candidate functions are the initializer-list constructors (§[dcl.init.list]) of the classTand the argument list consists of the initializer list as a single argument.— Otherwise, or 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.
Initializer list constructors are greedy, so even though A(int) constructor is available, the standard mandates that initializer_list is prioritized, and if - and only if - it's not available, the compiler is allowed to look for other constructors. (This is why it is not recommended to provide a constructor that ambiguously overloads with an initializer_list constructor. See the answer to #4 in https://herbsutter.com/2013/05/09/gotw-1-solution/.)