Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I have multiple parameter packs in a variadic template?

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. 
like image 938
Samuel Danielson Avatar asked Mar 22 '12 22:03

Samuel Danielson


People also ask

What is parameter pack c++?

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.

How do you expand a parameter pack?

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 , .

What is Variadic template in C++?

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.


2 Answers

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; } 
like image 193
Alexandre Hamez Avatar answered Sep 28 '22 07:09

Alexandre Hamez


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.

like image 40
2 revs Avatar answered Sep 28 '22 07:09

2 revs