Consider the code
#include <iostream>
class Foo
{
int val_;
public:
Foo(std::initializer_list<Foo> il)
{
std::cout << "initializer_list ctor" << std::endl;
}
/* explicit */ Foo(int val): val_(val)
{
std::cout << "ctor" << std::endl;
};
};
int main(int argc, char const *argv[])
{
// why is the initializer_list ctor invoked?
Foo foo {10};
}
The output is
ctor
initializer_list ctor
As far as I understand, the value 10
is implicitly converted to a Foo
(first ctor
output), then the initializer constructor kicks in (second initializer_list ctor
output). My question is why is this happening? Isn't the standard constructor Foo(int)
a better match? I.e., I would have expected the output of this snippet to be just ctor
.
PS: If I mark the constructor Foo(int)
as explicit
, then Foo(int)
is the only constructor invoked, as the integer 10
cannot now be implicitly converted to a Foo
.
§13.3.1.7 [over.match.list]/p1:
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.If the initializer list has no elements and
T
has a default constructor, the first phase is omitted. In copy-list-initialization, if anexplicit
constructor is chosen, the initialization is ill-formed.
As long as there is a viable initializer-list constructor, it will trump all non-initializer-list constructors when list-initialization is used and the initializer list has at least one element.