我怎样才能在一个可变参数模板中有多个参数包?

函数 一()接受一个参数包。函数 接受两个。每个包都被限制为包装在类型 AB中。为什么不可能实例化

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);
}

试过用 GCC 和 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.
38507 次浏览

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.

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;
}

Function templates (like skypjack's example) and partial specializations of class and variable templates can have multiple parameter packs if each template parameter subsequent to a template parameter pack either has a default value or can be deduced. The only thing I'd like to add/point out is that for class and variable templates you need a partial specialization. (See: C++ Templates, The Complete Guide, Vandevoorde, Josuttis, Gregor 12.2.4, Second Edition)

// A template to hold a parameter pack
template < typename... >
struct Typelist {};


// Declaration of a template
template< typename TypeListOne
, typename TypeListTwo
>
struct SomeStruct;


// Specialization of template with multiple parameter packs
template< typename... TypesOne
, typename... TypesTwo
>
struct SomeStruct< Typelist < TypesOne... >
, Typelist < TypesTwo... >
>
{
// Can use TypesOne... and TypesTwo... how ever
// you want here. For example:
typedef std::tuple< TypesOne... > TupleTypeOne;
typedef std::tuple< TypesTwo... > TupleTypeTwo;
};

The compiler needs a way to know where is the barrier between the two variadic templates. A clean way of doing this is to define one pack of arguments for an object and the second pack for a static member function. This can be appied to more than two variadic templates by nesting multiple structs in eachother. (keeping the last level as a function)

#include <iostream>


template<typename... First>
struct Obj
{
template<typename... Second>
static void Func()
{
std::cout << sizeof...(First) << std::endl;
std::cout << sizeof...(Second) << std::endl;
}
};


int main()
{
Obj<char, char>::Func<char, char, char, char>();
return 0;
}
two<>();
two<int>(a);
two<int>(a, b);

see https://www.youtube.com/watch?v=va9I2qivBOA 29:00-35:00