The below fails to compile with clang35 -std=c++11
:
#include
#include
#include
class A
{
The behavior makes sense. Scott Meyers has an example almost exactly like this in Effective Modern C++ (emphasis in original):
If, however, one or more constructors declare a parameter of type
std::initializer_list
, calls using the braced initialization syntax strongly prefer the overloads takingstd;:initializer_list
s. Strongly. If there's any way for compilers to construe a call using a braced initializer to be a constructor taking astd::initializer_list
, compilers will employ that interpretation.
Example using this class:
class Widget {
public:
Widget(int, bool);
Widget(int, double);
Widget(std::initializer_list);
};
Widget w1(10, true); // calls first ctor
Widget w2{10, true}; // calls std::initializer_list ctor
Widget w3(10, 5.0); // calls second ctor
Widget w4{10, 5.0}; // calls std::initializer_list ctor
Those two calls call the initializer_list
ctor even though they involve converting BOTH arguments - and even though the other constructors are perfect matches.
Furthermore:
Compilers' determination to match braced initializers with constructors taking
std::initializer_list
s is so strong, it prevails even if the best-matchstd::initializer_list
constructor can't be called. For example:class Widget { public: Widget(int, bool); // as before Widget(int, double); // as before Widget(std::initializer_list
); // now bool }; Widget w{10, 5.0}; // error! requires narrowing conversions
Both compilers pick the correct overload (the initializer_list
one) - which we can see is required from the standard (§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:(1.1) — 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.
(1.2) — If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the classT
and the argument list consists of the elements of the initializer list.
But calling that particular constructor involves a narrowing. In 8.5.1:
If the initializer-clause is an expression and a narrowing conversion (8.5.4) is required to convert the expression, the program is ill-formed.
So the program is ill-formed. In this case, clang chooses to throw an error while gcc chooses to issue a warning. Both compilers are conforming.