简体   繁体   中英

Why my variadic template constructor does not match its arguments?

My problem can be reproduced as this:

struct MyClass {
    template<typename...Ts>
    MyClass(Ts&&..., int) {};
};

int main() {
    MyClass mc{1, 2 }; // error: cannot convert from 'initializer list' to 'MyClass'
}

What's wrong with my code?

In a primary class template, the template parameter pack must be the final parameter in the template parameter list. In a function template, the template parameter pack may appear earlier in the list provided that all following parameters can be deduced from the function arguments, or have default arguments.

Remove the last parameter in the constructor and use brackets in construction if you want to pass list of ints, not initializer_list.

template<typename...Ts>
MyClass(Ts&&...) {};

MyClass mc(1, 2);

Parameter packs (and their deduction) are greedy. When you initialize your MyClass , the two integers go towards deducing the pack [int, int] and the last integer is left without a corresponding argument.

This makes the c'tor not viable in overload resolution. Since your class has no other constructors that could be used here, it's a hard error.

This is one reason why a parameter pack should usually go last, if we ever want the function to be viable after template argument deduction.

struct MyClass {
    template<typename...Ts>
    MyClass(int, Ts&&...) {}
};

int main() {
    MyClass mc{7,9,1}; // no more error
}

The reason is when you specify 'int' after 'Ts&&...' the compiler cannot deduce if the last type belongs to the parameter pack or not.

From http://en.cppreference.com/w/cpp/language/parameter_pack

In a primary class template, the template parameter pack must be the final parameter in the template parameter list. In a function template, the template parameter pack may appear earlier in the list provided that all following parameters can be deduced from the function arguments, or have default arguments:

template<typename... Ts, typename U> struct Invalid; // Error: Ts.. not at the end

template<typename ...Ts, typename U, typename=void>
void valid(U, Ts...);     // OK: can deduce U
// void valid(Ts..., U);  // Can't be used: Ts... is a non-deduced context in this position

valid(1.0, 1, 2, 3);      // OK: deduces U as double, Ts as {int,int,int}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM