85 votes

Comment puis-je avoir plusieurs packs de paramètres dans un modèle variadique ?

Fonction un() accepte un paquet de paramètres. Fonction deux() en accepte deux. Chaque paquet est contraint d'être enveloppé dans des types A y B . Pourquoi est-il impossible d'instancier deux() ?

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

J'ai 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.

57voto

Alexandre Hamez Points 1537

Voici une autre façon d'avoir plusieurs paquets de paramètres en utilisant les paramètres de modèles :

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

22voto

Samuel Danielson Points 1043

J'ai trouvé une solution. Envelopper chaque paquet de paramètres dans un Tuple. Utiliser un struct pour la spécialisation partielle. Voici une démo qui transmet les arguments à un foncteur en consommant un tuple comme une liste et en accumulant un autre. En fait, celui-ci transmet par copie. Les tuples sont utilisés dans la déduction de type, mais aucun tple n'est utilisé dans les paramètres de fonction, ce qui est très bien.

#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 en temps de compilation.

20voto

Mathematical Joe Points 141

Modèles de fonctions (comme l'exemple de skypjack) et spécialisations partielles des modèles de classes et de variables 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 j'aimerais ajouter ou souligner est que pour les modèles de classes et de variables, vous avez besoin d'une spécialisation partielle. (Voir : 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;
};

10voto

Nandee Points 439

Le compilateur a besoin d'un moyen de savoir où se trouve la barrière entre les deux modèles variadiques. Une façon propre de le faire est de définir un paquet d'arguments pour un objet et un second paquet pour une fonction membre statique. Ceci peut être appliqué à plus de deux modèles variadiques en imbriquant plusieurs structs les uns dans les autres. (en gardant le dernier niveau comme une fonction)

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

0voto

user1226313 Points 11

Il semble que je n'aie pas assez de réputation pour ajouter un commentaire au bon endroit, je vais donc utiliser la zone de réponse à la place.

Cette approche peut-elle également être appliquée aux modèles de classe ?

Je voudrais aussi savoir ceci, j'ai posté une question sur un cas spécifique aux classes dans lequel la solution fournie par Alexandre Hamez ne fonctionne pas : Comment donner plusieurs arguments de modèle variadique à une classe

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X