According to §8.5.4.3 in the C++ standard:
"List-initialization of an object or reference of type T is defined as follows:
— If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
— Otherwise, if T is an aggregate, aggregate initialization is performed (§8.5.1).
— Otherwise, if T is a specialization of std::initializer_list<E>, an initializer_list object is constructed as described below and used to initialize the object according to the rules for initialization of an object from a class of the same type (§8.5).
— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (§13.3, §13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
— (more cases...)"
— If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
— Otherwise, if T is an aggregate, aggregate initialization is performed (§8.5.1).
— Otherwise, if T is a specialization of std::initializer_list<E>, an initializer_list object is constructed as described below and used to initialize the object according to the rules for initialization of an object from a class of the same type (§8.5).
— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (§13.3, §13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
— (more cases...)"
a1
is default initialized, as described in §8.5.0.11a2
doesn't actually use the initializer_list constructor with a list of zero elements, but the default constructor, as described by the first option of the list above.a3
's and a4
's constructor is chosen in overload resolution, as described in §13.3.1.7:
"When objects of non-aggregate class type T are list-initialized (§8.5.4), overload resolution selects the constructor in two phases:
— Initially, the candidate functions are the initializer-list constructors (§8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
— 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."
— Initially, the candidate functions are the initializer-list constructors (§8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
— 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<int>
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 http://herbsutter.com/2013/05/09/gotw-1-solution/ )#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 }; } // 1133
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.