Quelles sont les règles du jeton «…» dans le contexte des modèles variadiques?

98

En C ++ 11, il existe des modèles variadiques comme celui-ci:

template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args )
{
    return unique_ptr<T>(new T(std::forward<Args>(args)...));
}

Il y a quelques curiosités à ce sujet: L'expression std::forward<Args>(args)...utilise les deux Argset argsun seul ...jeton. De plus, std::forwardune fonction de modèle non variadique ne prend qu'un seul paramètre de modèle et un argument. Quelles sont les règles de syntaxe pour cela (en gros)? Comment peut-il être généralisé?

Aussi: Dans l'implémentation de la fonction, les points de suspension ( ...) se trouvent à la fin de l'expression d'intérêt. Y a-t-il une raison pour laquelle, dans la liste des arguments de modèle et la liste des paramètres, les points de suspension sont au milieu?

Ralph Tandetzky
la source
2
Brièvement sur la deuxième partie: Lors de la "déclaration" d'un pack de paramètres de modèle ou d'un pack de paramètres de fonction, l ' ...vient avant que l'identifiant soit introduit. Lors de l'utilisation de l'un ou des deux types de packs, le ...se trouve après le modèle d'expression à développer.
aschepler

Réponses:

99

Dans le contexte du modèle variadique, les points de suspension ...sont utilisés pour décompresser le pack de paramètres du modèle s'il apparaît sur le côté droit d'une expression (appelez ce modèle d' expression pendant un moment). La règle est que tout motif se trouvant sur le côté gauche de ...est répété - les motifs décompressés (appelez-les maintenant expressions ) sont séparés par des virgules ,.

Il peut être mieux compris par quelques exemples. Supposons que vous ayez ce modèle de fonction:

template<typename ...T>
void f(T ... args) 
{
   g( args... );        //pattern = args
   h( x(args)... );     //pattern = x(args)
   m( y(args...) );     //pattern = args (as argument to y())
   n( z<T>(args)... );  //pattern = z<T>(args)
}

Maintenant, si j'appelle cette fonction en passant Tcomme {int, char, short}, alors chacun des appels de fonction est développé comme:

g( arg0, arg1, arg2 );           
h( x(arg0), x(arg1), x(arg2) );
m( y(arg0, arg1, arg2) );
n( z<int>(arg0), z<char>(arg1), z<short>(arg2) );

Dans le code que vous avez publié, std::forwardsuit le quatrième modèle illustré par n()l'appel de fonction.

Notez la différence entre x(args)...et y(args...)au - dessus!


Vous pouvez également utiliser ...pour initialiser un tableau comme:

struct data_info
{
     boost::any  data;
     std::size_t type_size;
};

std::vector<data_info> v{{args, sizeof(T)}...}; //pattern = {args, sizeof(T)}

qui est étendu à ceci:

std::vector<data_info> v 
{ 
   {arg0, sizeof(int)},
   {arg1, sizeof(char)},
   {arg2, sizeof(short)}
};

Je viens de réaliser qu'un modèle pourrait même inclure un spécificateur d'accès tel que public, comme indiqué dans l'exemple suivant:

template<typename ... Mixins>
struct mixture : public Mixins ...  //pattern = public Mixins
{
    //code
};

Dans cet exemple, le modèle est développé comme:

struct mixture__instantiated : public Mixin0, public Mixin1, .. public MixinN  

Autrement dit, mixturedérive publiquement de toutes les classes de base.

J'espère que cela pourra aider.

Nawaz
la source
1
Concernant l'expression qui correspond; Ce devrait être l' expression la plus large possible , n'est-ce pas? Par exemple, x+args...devrait être étendu à x+arg0,x+arg1,x+arg2, non x+arg0,arg1,arg2.
bitmask
Donc, le ...s'applique à chaque entité extensible dans le modèle.
Courses de légèreté en orbite le
@bitmask: Oui. x+args...devrait s'étendre à x+arg0,x+arg1,x+arg2, non x+arg0,arg1,arg2 .
Nawaz
1
@ Jarod42: Je mettrai à jour cette réponse une fois que C ++ 17 sera publié.
Nawaz
3
@synther: sizeof...(T)n'est pas nécessaire là-bas. Vous pouvez simplement écrire:int a[] = { ___ };
Nawaz
48

Ce qui suit est tiré de la conférence «Les modèles variadiques sont Funadic» par Andrei Alexandrescu à GoingNative 2012. Je peux le recommander pour une bonne introduction sur les modèles variadiques.


Il y a deux choses que l'on peut faire avec un pack variadic. Il est possible de postuler sizeof...(vs)pour obtenir le nombre d'éléments et l'élargir.

Règles d'extension

Use            Expansion

Ts...          T1, ..., Tn
Ts&&...        T1&&, ..., Tn&&
x<Ts,Y>::z...  x<T1,Y>::z, ..., x<Tn,Y>::z
x<Ts&,Us>...   x<T1&,U1>, ..., x<Tn&,Un>
func(5,vs)...  func(5,v1), ..., func(5,vn)

L'expansion se poursuit vers l'intérieur vers l'extérieur. Lorsque vous développez deux listes en mode verrouillé, elles doivent avoir la même taille.

Plus d'exemples:

gun(A<Ts...>::hun(vs)...);

Développe tout Tsdans la liste d'arguments du modèle de A, puis la fonction hunse développe avec tout vs.

gun(A<Ts...>::hun(vs...));

Développe tout Tsdans la liste d'arguments du modèle de Aet all vscomme arguments de fonction pour hun.

gun(A<Ts>::hun(vs)...);

Étend la fonction hunavec Tset vsen lock-step.

Remarque:

Tsn'est pas un type et vsn'est pas une valeur! Ce sont des alias pour une liste de types / valeurs. Les deux listes peuvent être potentiellement vides. Les deux n'obéissent qu'à des actions spécifiques. Donc ce qui suit n'est pas possible:

typedef Ts MyList;  // error!
Ts var;             // error!
auto copy = vs;     // error!

Loci d'expansion

Arguments de fonction

template <typename... Ts>
void fun(Ts... vs)

Listes d'initialisation

any a[] = { vs... };

Spécificateurs de base

template <typename... Ts>
struct C : Ts... {};
template <typename... Ts>
struct D : Box<Ts>... { /**/ };

Listes d'initialisation des membres

// Inside struct D
template <typename... Us>
D(Us... vs) : Box<Ts>(vs)... {}

Listes d'arguments Tempate

std::map<Ts...> m;

Ne compilera que s'il y a une correspondance possible pour les arguments.

Capturer des listes

template <class... Ts> void fun(Ts... vs) {
    auto g = [&vs...] { return gun(vs...); }
    g();
}

Listes d'attributs

struct [[ Ts... ]] IAmFromTheFuture {};

C'est dans la spécification, mais il n'y a pas encore d'attribut qui puisse être exprimé comme un type.

typ1232
la source
Agréable. Les arguments de fonction sont laissés en dehors des loci, cependant: P
Potatoswatter
@Potatoswatter Merci. L'expansion de la liste des arguments de la fonction n'a pas été mentionnée.
typ1232
@ typ1232 Désolé pour la modification, mais j'ai estimé que votre message d'origine était trop proche du plagiat. Btw, j'ai aussi regardé ce discours il y a quelque temps - génial.
Walter le