Search code examples
c++templatesc++11initializer-listvariadic

List-initializer and variadic constructor


From CPP reference on list-initialisation:

Otherwise, the constructors of T are considered, in two phases:

  • All constructors that take std::initializer_list as the only argument, or as the first argument if the remaining arguments have default values, are examined, and matched by overload resolution against a single argument of type std::initializer_list

  • If the previous stage does not produce a match, all constructors of T participate in overload resolution against the set of arguments that consists of the elements of the braced-init-list, with the restriction that only non-narrowing conversions are allowed. If this stage produces an explicit constructor as the best match for a copy-list-initialization, compilation fails (note, in simple copy-initialization, explicit constructors are not considered at all)

So a constructor using initializer_list is considered first. Failing that, each element of the list is considered as arguments for constructors. However

#include <iostream>

using namespace std;

struct A{
    template <typename... Args> A(Args... li) { cout << sizeof...(Args) << endl;}
};

int main(){

    A a = {2,3,4};

}

The output is 3 which indicates that Args... unpacks as int, int, int. Why is it that Args... was not simply made the singular initializer_list<int>, which the details on list-initialisation indicated would be the first attempted type of constructor?


Solution

  • [temp.deduct.call]/1 Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std::initializer_list<P'> for some P' and the argument is an initializer list (8.5.4), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (14.8.2.5).

    Emphasis mine. For this reason, template argument deduction for the constructor from an argument of type initializer_list<int> fails.