82 votes

Variadic template pack d'extension

J'ai essayé d'apprendre le variadic templates et les fonctions. Je ne peux pas comprendre, pourquoi ce code ne compile pas:

template<typename T>
static void bar(T t) {}

template<typename... Args>
static void foo2(Args... args)
{
    (bar(args)...);
}

int main()
{
    foo2(1, 2, 3, "3");
    return 0;    
}

La Compilation échoue avec l'erreur: erreur c3520: 'args': paramètre pack doit être élargi dans ce contexte(en fonction foo2).

136voto

T.C. Points 22510

L'un des endroits où un pack d'extension peut se produire à l'intérieur d'un arc-boutée-init-liste. Vous pouvez prendre avantage de cela en mettant l'expansion à l'intérieur de la liste d'initialiseur d'un mannequin tableau:

template<typename... Args>
static void foo2(Args &&... args)
{
    int dummy[] = { 0, ( (void) bar(std::forward<Args>(args)), 0) ... };
}

Afin d'expliquer le contenu de l'initialiseur plus en détail:

{ 0, ( (void) bar(std::forward<Args>(args)), 0) ... };
  |       |       |                        |     |
  |       |       |                        |     --- pack expand the whole thing 
  |       |       |                        |   
  |       |       --perfect forwarding     --- comma operator
  |       |
  |       -- cast to void to ensure that regardless of bar()'s return type
  |          the built-in comma operator is used rather than an overloaded one
  |
  ---ensure that the array has at least one element so that we don't try to make an
     illegal 0-length array when args is empty

La démo.

40voto

Angew Points 53063

Paramètre packs ne peut être élargie à un usage strictement défini par la liste de contextes, et l'opérateur , n'est pas l'un d'eux. En d'autres termes, il n'est pas possible d'utiliser le pack d'extension pour générer une expression consistant en une série de sous-expressions délimité par l'opérateur ,.

La règle de base est "l'Expansion peut générer une liste d' ,-séparé, modèles où les , est une liste de délimitation." Opérateur de , ne permet pas de construire une liste, dans le sens de la grammaire.

Pour appeler une fonction pour chaque argument, vous pouvez utiliser la récursivité (qui est l'outil principal de la variadic template du programmeur de la boîte):

template <typename T>
void bar(T t) {}

void foo2() {}

template <typename Car, typename... Cdr>
void foo2(Car car, Cdr... cdr)
{
  bar(car);
  foo2(cdr...);
}

int main()
{
  foo2 (1, 2, 3, "3");
}

Live exemple

17voto

CoffeeandCode Points 1123

Honteuse COPIE [approuvé par sa source]

Paramètre packs ne peut être élargie à un usage strictement défini par la liste de contextes, et l'opérateur , n'est pas l'un d'eux. En d'autres termes, il n'est pas possible d'utiliser le pack d'extension pour générer une expression consistant en une série de sous-expressions délimité par l'opérateur ,.

La règle de base est "l'Expansion pouvez générer une liste d' ,-séparé, modèles où les , est une liste de délimitation." Opérateur de , ne permet pas de construire une liste, dans le sens de la grammaire.

Pour appeler une fonction pour chaque argument, vous pouvez utiliser la récursivité (qui est l'outil principal de la variadic template du programmeur de la boîte):

#include <utility>

template<typename T>
void bar(T &&t){}

void foo(){}

template<typename Arg, typename ... Args>
void foo(Arg &&arg, Args &&... args)
{
    bar(std::forward<Arg>(arg));
    foo(std::forward<Args>(args)...);
}

int main()
{
  foo2 (1, 2, 3, "3");
}

UTILE NON COPIÉ INFO

Une autre chose que vous n'avez probablement pas vu dans cette réponse, c'est l'utilisation de l' && le prescripteur et le std::forward. En C++, l' && spécificateur de cam signifie 2 choses: rvalue-des références ou des références universelles.

Je n'entrerai pas dans les rvalue-références, mais pour quelqu'un travaillant avec les variadic templates; des références universelles sont un dieu-envoyer.

Le Transfert Parfait

L'une des utilisations de l' std::forward et des références universelles sont le transfert parfait de types à d'autres fonctions.

Dans votre exemple, si on passe un int& de foo2 il sera automatiquement rétrogradé à l' int en raison de la signature de l'générées foo2 fonction après le modèle de déduction et si vous voulais donc avant cette arg à une autre fonction qui permettrait de modifier il ny de référence, vous obtiendrez des résultats indésirables (la variable ne sera pas changé) car foo2 sera de passer une référence à la temporaire créé par le passage d'un int pour elle. Pour contourner ce problème, nous spécifier une fonction de transmission de prendre tout type de référence à une variable (rvalue ou lvalue). Alors, pour être sûr que nous passons le type exact passé dans la fonction de transmission, nous utilisons std::forward, alors et seulement alors ne nous laissons la rétrogradation de types; parce que nous sommes maintenant au point où c'est le plus important.

Si vous avez besoin pour, lire la suite sur des références universelles et de transfert parfait; scott meyers est assez grand comme une ressource.

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