Fonction n () accepte un pack de paramètres. Fonction deux () accepte deux. Chaque pack est contraint d'être enveloppé dans des types A et B . Pourquoi est-il impossible d'instancier 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);
}
Essayé avec gcc et 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.
Voici une autre façon d'avoir plusieurs packs de paramètres à l'aide de paramètres de modèle de modèle:
#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;
}
J'ai trouvé une solution. Enveloppez chaque pack de paramètres dans un tuple. Utilisez une structure pour une spécialisation partielle. Voici une démo qui transmet des arguments à un foncteur en consommant un Tuple en tant que liste et en en accumulant un autre. Eh bien, celui-ci avance en copiant. Les tuples sont utilisés dans la déduction de type mais aucun tuples n'est utilisé dans les paramètres de fonction, ce qui, je pense, est net.
#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);
}
Les tuples sont une très bonne liste de temps de compilation.
Les modèles de fonction (comme l'exemple de skypjack) et spécialisations partielles des modèles de classe et de variable peuvent avoir plusieurs packs de paramètres si chaque paramètre de modèle suivant un pack de paramètres de modèle a une valeur par défaut ou peut être déduit. La seule chose que je voudrais ajouter/souligner, c'est que pour les modèles de classe et de variable, vous avez besoin d'une spécialisation partielle. (Voir: Modèles C++, 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;
};