Function one() accepts one parameter pack. Function two() accepts two. Each pack is constrained to be wrapped in types A and B. Why is it impossible to instantiate two()?
template <typename T> struct A {}; template <typename T> struct B {}; template <typename... Ts> void one(A<Ts> ...as) { } template <typename... Ts, typename... Us> void two(A<Ts> ...as, B<Us> ...bs) { } int main() { auto a = A<int>(); auto b = B<int>(); // Just fine one(); one(a); one(a, a); // All errors two(); two(a); two(a, b); }
Tried with gcc and clang.
sam@wish:~/x/cpp$ gcc -std=c++0x variadic_templates.cpp variadic_templates.cpp: In function ‘int main()’: variadic_templates.cpp:23:7: error: no matching function for call to ‘two()’ variadic_templates.cpp:23:7: note: candidate is: variadic_templates.cpp:11:6: note: template<class ... Ts, class ... Us> void two(A<Ts>..., B<Us>...) variadic_templates.cpp:24:8: error: no matching function for call to ‘two(A<int>&)’ variadic_templates.cpp:24:8: note: candidate is: variadic_templates.cpp:11:6: note: template<class ... Ts, class ... Us> void two(A<Ts>..., B<Us>...) variadic_templates.cpp:25:11: error: no matching function for call to ‘two(A<int>&, B<int>&)’ variadic_templates.cpp:25:11: note: candidate is: variadic_templates.cpp:11:6: note: template<class ... Ts, class ... Us> void two(A<Ts>..., B<Us>...) sam@wish:~/x/cpp$ clang -std=c++0x variadic_templates.cpp variadic_templates.cpp:23:3: error: no matching function for call to 'two' two(); ^~~ variadic_templates.cpp:11:6: note: candidate function template not viable: requires at least 1 argument, but 0 were provided void two(A<Ts> ...as, B<Us> ...bs) {} ^ variadic_templates.cpp:24:3: error: no matching function for call to 'two' two(a); ^~~ variadic_templates.cpp:11:6: note: candidate function not viable: requires 0 arguments, but 1 was provided void two(A<Ts> ...as, B<Us> ...bs) {} ^ variadic_templates.cpp:25:3: error: no matching function for call to 'two' two(a, b); ^~~ variadic_templates.cpp:11:6: note: candidate function not viable: requires 0 arguments, but 2 were provided void two(A<Ts> ...as, B<Us> ...bs) {} ^ 3 errors generated.
A template parameter pack is a template parameter that accepts zero or more template arguments (non-types, types, or templates). A function parameter pack is a function parameter that accepts zero or more function arguments.
Parameter packs can only be expanded in a strictly-defined list of contexts, and operator , is not one of them. In other words, it's not possible to use pack expansion to generate an expression consisting of a series of subexpressions delimited by operator , .
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.
Here is another way to have several parameters packs using template template parameters:
#include <iostream> template <typename... Types> struct foo {}; template < typename... Types1, template <typename...> class T , typename... Types2, template <typename...> class V , typename U > void bar(const T<Types1...>&, const V<Types2...>&, const U& u) { std::cout << sizeof...(Types1) << std::endl; std::cout << sizeof...(Types2) << std::endl; std::cout << u << std::endl; } int main() { foo<char, int, float> f1; foo<char, int> f2; bar(f1, f2, 9); return 0; }
I found one solution. Wrap each parameter pack in a Tuple. Use a struct for partial specialization. Here's a demo that forwards arguments to a functor by consuming one tuple as a list and accumulating another. Well, this one forwards by copying. Tuples are used in type deduction yet no tuples are used in function parameters, which I think is neat.
#include <iostream> #include <tuple> template < typename ... > struct two_impl {}; // Base case template < typename F, typename ...Bs > struct two_impl < F, std::tuple <>, std::tuple< Bs... > > { void operator()(F f, Bs... bs) { f(bs...); } }; // Recursive case template < typename F, typename A, typename ...As, typename ...Bs > struct two_impl < F, std::tuple< A, As... >, std::tuple< Bs...> > { void operator()(F f, A a, As... as, Bs... bs) { auto impl = two_impl < F, std::tuple < As... >, std::tuple < Bs..., A> >(); impl(f, as..., bs..., a); } }; template < typename F, typename ...Ts > void two(F f, Ts ...ts) { auto impl = two_impl< F, std::tuple < Ts... >, std::tuple <> >(); impl(f, ts...); } struct Test { void operator()(int i, float f, double d) { std::cout << i << std::endl << f << std::endl << d << std::endl; } }; int main () { two(Test(), 1, 1.5f, 2.1); }
Tuples are a very good compile time list.
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