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
.
An object of type std::initializer_list<T> is a lightweight proxy object that provides access to an array of objects of type const T .
Initializer List is used in initializing the data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon. Following is an example that uses the initializer list to initialize x and y of Point class.
Initialization lists allow you to choose which constructor is called and what arguments that constructor receives. If you have a reference or a const field, or if one of the classes used does not have a default constructor, you must use an initialization list.
The most common benefit of doing this is improved performance. If the expression whatever is the same type as member variable x_, the result of the whatever expression is constructed directly inside x_ — the compiler does not make a separate copy of the object.
§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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With