Namespaces
Variants

Pack (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Template specialization
Parameter packs (C++11)
Miscellaneous

Un pack est une entité C++ qui définit l'un des éléments suivants :

  • un paramètre pack
  • template parameter pack
  • function parameter pack
**Note:** Le texte a été laissé en anglais car il s'agit de termes spécifiques au C++ qui ne sont généralement pas traduits dans la documentation technique française pour préserver leur précision technique.
(depuis C++20)
(depuis C++26)

Un paramètre de modèle variadique est un paramètre de modèle qui accepte zéro ou plusieurs arguments de modèle (constantes, types ou modèles). Un paramètre de fonction variadique est un paramètre de fonction qui accepte zéro ou plusieurs arguments de fonction.

Une capture d'initialisation de pack lambda est une capture lambda qui introduit une capture d'initialisation pour chaque élément dans l'expansion du pack de son initialiseur.

(depuis C++20)

Une liaison structurée pack est un identifiant dans la déclaration de liaison structurée qui introduit zéro ou plusieurs liaisons structurées.

(since C++26)

Le nombre d'éléments d'un pack est égal à :

  • le nombre d'arguments fournis pour le pack de paramètres, si le pack est un pack de paramètres de modèle ou de fonction,
  • le nombre d'éléments dans l'expansion du pack de son initialiseur, si le pack est un pack de capture d'initialisation de lambda,
(depuis C++20)
  • taille de la liaison structurée de l'initialiseur moins le nombre d'éléments non-pack dans la déclaration de liaison structurée, si le pack est un pack de liaison structurée.
(depuis C++26)

Un modèle avec au moins un paquet de paramètres est appelé un modèle variadique .

Table des matières

Syntaxe

Pack de paramètres de modèle (apparaît dans les listes de paramètres des alias template , class template , variable template (depuis C++14) , concept (depuis C++20) et function template )

type ... pack-name  (optionnel) (1)
typename | class ... pack-name  (optionnel) (2)
type-constraint ... pack-name  (optionnel) (3) (depuis C++20)
template < parameter-list > class ... pack-name  (optionnel) (4) (jusqu'à C++17)
template < parameter-list > typename | class ... pack-name  (optionnel) (4) (depuis C++17)

Fonction parameter pack (une forme de declarator , apparaît dans une liste de paramètres de fonction d'un modèle de fonction variadique)

pack-name ... pack-param-name  (facultatif) (5)

Pour la syntaxe des packs non-paramètres, voir lambda init-capture pack et structured binding pack (depuis C++26) .

(depuis C++20)

Expansion de pack (apparaît dans le corps d'un template)

pattern ... (6)
1) Un paramètre de modèle variadique constant avec un nom facultatif
2) Un paramètre de modèle de type variadique avec un nom facultatif
(depuis C++20)
4) Un paramètre template template pack avec un nom optionnel
5) Un paramètre de fonction pack avec un nom optionnel
6) Expansion de paquet : se développe en une liste de zéro ou plusieurs pattern s. Le motif doit inclure au moins un paquet.

Explication

Un modèle de classe variadique peut être instancié avec n'importe quel nombre d'arguments de modèle :

template<class... Types>
struct Tuple {};
Tuple<> t0;           // Types ne contient aucun argument
Tuple<int> t1;        // Types contient un argument : int
Tuple<int, float> t2; // Types contient deux arguments : int et float
Tuple<0> t3;          // erreur : 0 n'est pas un type

Une fonction template variadique peut être appelée avec n'importe quel nombre d'arguments de fonction (les arguments template sont déduits via template argument deduction ):

template<class... Types>
void f(Types... args);
f();       // OK : args ne contient aucun argument
f(1);      // OK : args contient un argument : int
f(2, 1.0); // OK : args contient deux arguments : int et double

Dans un modèle de classe primaire, le paquet de paramètres de modèle doit être le dernier paramètre dans la liste des paramètres de modèle. Dans un modèle de fonction, le paquet de paramètres de modèle peut apparaître plus tôt dans la liste à condition que tous les paramètres suivants puissent être déduits des arguments de la fonction, ou aient des arguments par défaut :

template<typename U, typename... Ts>    // OK : peut déduire U
struct valid;
// template<typename... Ts, typename U> // Erreur : Ts... pas à la fin
// struct Invalid;
template<typename... Ts, typename U, typename=void>
void valid(U, Ts...);    // OK : peut déduire U
// void valid(Ts..., U); // Ne peut pas être utilisé : Ts... est un contexte non déduit dans cette position
valid(1.0, 1, 2, 3);     // OK : déduit U comme double, Ts comme {int, int, int}

Si toute spécialisation valide d'un modèle variadique nécessite un pack de paramètres de modèle vide, le programme est mal formé, aucun diagnostic requis.

Expansion de pack

Un motif suivi de points de suspension, dans lequel le nom d'au moins un pack apparaît au moins une fois, est développé en zéro ou plusieurs instanciations du motif, où le nom du pack est remplacé par chacun des éléments du pack, dans l'ordre. Les instanciations des spécificateurs d'alignement sont séparées par des espaces, les autres instanciations sont séparées par des virgules.

template<class... Us>
void f(Us... pargs) {}
template<class... Ts>
void g(Ts... args)
{
    f(&args...); // « &args... » est une expansion de pack
                 // « &args » est son motif
}
g(1, 0.2, "a"); // Ts... args se développe en int E1, double E2, const char* E3
                // &args... se développe en &E1, &E2, &E3
                // Us... pargs se développe en int* E1, double* E2, const char** E3

Si les noms de deux paquets apparaissent dans le même motif, ils sont développés simultanément et doivent avoir la même longueur :

template<typename...>
struct Tuple {};
template<typename T1, typename T2>
struct Pair {};
template<class... Args1>
struct zip
{
    template<class... Args2>
    struct with
    {
        typedef Tuple<Pair<Args1, Args2>...> type;
        // Pair<Args1, Args2>... est l'expansion de pack
        // Pair<Args1, Args2> est le motif
    };
};
typedef zip<short, int>::with<unsigned short, unsigned>::type T1;
// Pair<Args1, Args2>... se développe en
// Pair<short, unsigned short>, Pair<int, unsigned int> 
// T1 est Tuple<Pair<short, unsigned short>, Pair<int, unsigned>>
// typedef zip<short>::with<unsigned short, unsigned>::type T2;
// erreur : l'expansion de pack contient des packs de longueurs différentes

Si une expansion de paquet est imbriquée dans une autre expansion de paquet, les paquets qui apparaissent à l'intérieur de l'expansion de paquet la plus interne sont développés par celle-ci, et il doit y avoir un autre paquet mentionné dans l'expansion de paquet englobante, mais pas dans la plus interne :

template<class... Args>
void g(Args... args)
{
    f(const_cast<const Args*>(&args)...); 
    // const_cast<const Args*>(&args) est le motif, il développe deux packs
    // (Args et args) simultanément
    f(h(args...) + args...); // Expansion de pack imbriquée :
    // l'expansion du pack interne est "args...", elle est développée en premier
    // l'expansion du pack externe est h(E1, E2, E3) + args..., elle est développée
    // ensuite (comme h(E1, E2, E3) + E1, h(E1, E2, E3) + E2, h(E1, E2, E3) + E3)
}

Lorsque le nombre d'éléments dans un pack est nul (pack vide), l'instanciation d'une expansion de pack ne modifie pas l'interprétation syntaxique de la construction englobante, même dans les cas où omettre complètement l'expansion de pack serait autrement mal formé ou entraînerait une ambiguïté syntaxique. L'instanciation produit une liste vide.

template<class... Bases> 
struct X : Bases... { };
template<class... Args> 
void f(Args... args) 
{
    X<Args...> x(args...);
}
template void f<>(); // OK, X<> n'a pas de classes de base
                     // x est une variable de type X<> qui est initialisée par valeur

Foyers d'expansion

Selon l'endroit où l'expansion a lieu, la liste résultante séparée par des virgules (ou séparée par des espaces pour les spécificateurs d'alignement ) est un type différent de liste : liste de paramètres de fonction, liste d'initialisation de membres, liste d'attributs, etc. Voici la liste de tous les contextes autorisés :

Listes d'arguments de fonction

Une expansion de paquet peut apparaître à l'intérieur des parenthèses d'un opérateur d'appel de fonction, auquel cas la plus grande expression ou liste d'initialisation entre accolades à gauche des points de suspension est le motif qui est développé :

f(args...);              // se développe en f(E1, E2, E3)
f(&args...);             // se développe en f(&E1, &E2, &E3)
f(n, ++args...);         // se développe en f(n, ++E1, ++E2, ++E3);
f(++args..., n);         // se développe en f(++E1, ++E2, ++E3, n);
f(const_cast<const Args*>(&args)...);
// f(const_cast<const E1*>(&X1), const_cast<const E2*>(&X2), const_cast<const E3*>(&X3))
f(h(args...) + args...); // se développe en 
// f(h(E1, E2, E3) + E1, h(E1, E2, E3) + E2, h(E1, E2, E3) + E3)

Initialiseurs entre parenthèses

Une expansion de paquet peut apparaître à l'intérieur des parenthèses d'un initialiseur direct , d'une conversion de type fonctionnelle , et d'autres contextes ( initialiseur de membre , nouvelle-expression , etc.) auquel cas les règles sont identiques aux règles pour une expression d'appel de fonction ci-dessus :

Class c1(&args...);             // appelle Class::Class(&E1, &E2, &E3)
Class c2 = Class(n, ++args...); // appelle Class::Class(n, ++E1, ++E2, ++E3);
::new((void *)p) U(std::forward<Args>(args)...) // std::allocator::allocate

Initialiseurs entre accolades

Dans une liste d'initialisation entre accolades, une expansion de paquet peut également apparaître :

template<typename... Ts>
void func(Ts... args)
{
    const int size = sizeof...(args) + 2;
    int res[size] = {1, args..., 2};
    // puisque les listes d'initialisation garantissent le séquencement, cela peut être utilisé pour
    // appeler une fonction sur chaque élément d'un pack, dans l'ordre :
    int dummy[sizeof...(Ts)] = {(std::cout << args, 0)...};
}

Listes d'arguments de template

Les expansions de paquets peuvent être utilisées n'importe où dans une liste d'arguments de template, à condition que le template ait les paramètres correspondant à l'expansion :

template<class A, class B, class... C>
void func(A arg1, B arg2, C... arg3)
{
    container<A, B, C...> t1; // se développe en container<A, B, E1, E2, E3> 
    container<C..., A, B> t2; // se développe en container<E1, E2, E3, A, B> 
    container<A, C..., B> t3; // se développe en container<A, E1, E2, E3, B> 
}

Liste des paramètres de fonction

Dans une liste de paramètres de fonction, si une ellipse apparaît dans une déclaration de paramètre (qu'elle nomme un pack de paramètres de fonction (comme dans, Args ... args ) ou non) la déclaration de paramètre est le motif :

template<typename... Ts>
void f(Ts...) {}
f('a', 1); // Ts... se développe en void f(char, int)
f(0.1);    // Ts... se développe en void f(double)
template<typename... Ts, int... N>
void g(Ts (&...arr)[N]) {}
int n[1];
g<const char, int>("a", n); // Ts (&...arr)[N] se développe en 
                            // const char (&)[2], int(&)[1]

Note : Dans le motif Ts (&...arr)[N] , l'ellipse est l'élément le plus interne, et non le dernier élément comme dans toutes les autres expansions de paquets.

Note : Ts (&...)[N] n'est pas autorisé car la grammaire C++11 exige que l'ellipse entre parenthèses ait un nom : CWG issue 1488 .

Liste des paramètres de modèle

L'expansion de paquet peut apparaître dans une liste de paramètres de modèle :

template<typename... T>
struct value_holder
{
    template<T... Values> // se développe en une liste de paramètres de template constants
    struct apply {};      // telle que <int, char, int(&)[5]>
};

Spécificateurs de base et listes d'initialisation des membres

Une expansion de paquet peut désigner la liste des classes de base dans une déclaration de classe . Typiquement, cela signifie également que le constructeur doit utiliser une expansion de paquet dans la liste d'initialisation des membres pour appeler les constructeurs de ces bases :

template<class... Mixins>
class X : public Mixins...
{
public:
    X(const Mixins&... mixins) : Mixins(mixins)... {}
};

Captures de lambda

L'expansion de paquet peut apparaître dans la clause de capture d'une expression lambda :

template<class... Args>
void f(Args... args)
{
    auto lm = [&, args...] { return g(args...); };
    lm();
}

L'opérateur sizeof...

L'opérateur sizeof... est également classifié comme une expansion de paquet :

template<class... Types>
struct count
{
    static const std::size_t value = sizeof...(Types);
};
**Note:** Le code C++ n'a pas été traduit car il se trouve dans des balises `
` et contient des termes spécifiques au C++ qui doivent être préservés. Seul le texte environnant (s'il y en avait) aurait été traduit en français.

Spécifications d'exceptions dynamiques

La liste des exceptions dans une spécification d'exception dynamique peut également être une expansion de pack :

template<class... X>
void func(int arg) throw(X...)
{
    // ... throw different Xs in different situations
}
(jusqu'à C++17)

Spécificateur d'alignement

Les expansions de paquets sont autorisées à la fois dans les listes de types et les listes d'expressions utilisées par le mot-clé alignas . Les instanciations sont séparées par des espaces :

template<class... T>
struct Align
{
    alignas(T...) unsigned char buffer[128];
};
Align<int, short> a; // les spécificateurs d'alignement après expansion sont
                     // alignas(int) alignas(short)
                     // (aucune virgule entre les deux)

Liste des attributs

Les expansions de paquets sont autorisées dans les listes d' attributs , si cela est permis par la spécification de l'attribut. Par exemple :

template<int... args>
[[vendor::attr(args)...]] void* f();
**Note:** Le code C++ n'a pas été traduit conformément aux instructions, car il se trouve dans des balises `
` et contient des termes spécifiques au C++ qui doivent être préservés. Seul le texte environnant (comme ce commentaire) a été traduit en français.

Expressions de repli

Dans les expressions de repli , le motif est la sous-expression entière qui ne contient pas de paquet non développé.

Déclarations using

Dans les déclarations using , des points de suspension peuvent apparaître dans la liste des déclarateurs, ce qui est utile lors de la dérivation d'un paquet de paramètres de modèle :

template<typename... bases>
struct X : bases...
{
    using bases::g...;
};
X<B, D> x; // OK: B::g and D::g introduced
(depuis C++17)


Indexation de paquets

Dans l' indexation de paquets , l'expansion de paquet contient un paquet non développé suivi de points de suspension et d'un indice. Le motif de l'expression d'indexation de paquet est un identifiant , tandis que le motif du spécificateur d'indexation de paquet est un typedef-name .

consteval auto first_plus_last(auto... args)
{
    return args...[0] + args...[sizeof...(args) - 1];
}
static_assert(first_plus_last(5) == 10);
static_assert(first_plus_last(5, 4) == 9);
static_assert(first_plus_last(5, 6, 2) == 7);

Déclarations friend

Dans les déclarations friend de classe, chaque spécificateur de type peut être suivi de points de suspension :

struct C {};
struct E { struct Nested; };
template<class... Ts>
class R
{
    friend Ts...;
};
template<class... Ts, class... Us>
class R<R<Ts...>, R<Us...>>
{
    friend Ts::Nested..., Us...;
};
R<C, E> rce;           // classes C and E are friends of R<C, E>
R<R<E>, R<C, int>> rr; // E::Nested and C are friends of R<R<E>, R<C, int>>

Contraintes développées par pliage

Dans les contraintes développées par pliage , le motif est la contrainte de cette contrainte développée par pliage.

Une contrainte développée par pliage n'est pas instanciée.

(depuis C++26)

Notes

Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_variadic_templates 200704L (C++11) Modèles variadiques
__cpp_pack_indexing 202311L (C++26) Indexation de paquets

Exemple

L'exemple ci-dessous définit une fonction similaire à std::printf , qui remplace chaque occurrence du caractère % dans la chaîne de format par une valeur.

La première surcharge est appelée lorsque seul le format de chaîne est passé et qu'il n'y a pas d'expansion de paramètre.

La deuxième surcharge contient un paramètre de modèle distinct pour la tête des arguments et un paquet de paramètres, cela permet à l'appel récursif de ne passer que la queue des paramètres jusqu'à ce qu'elle devienne vide.

Targs est le paramètre de modèle pack et Fargs est le paramètre de fonction pack.

#include <iostream>
void tprintf(const char* format) // fonction de base
{
    std::cout << format;
}
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // fonction variadique récursive
{
    for (; *format != '\0'; format++)
    {
        if (*format == '%')
        {
            std::cout << value;
            tprintf(format + 1, Fargs...); // appel récursif
            return;
        }
        std::cout << *format;
    }
}
int main()
{
    tprintf("% world% %\n", "Hello", '!', 123);
}

Sortie :

Hello world! 123

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Applicable à Comportement publié Comportement corrigé
CWG 1533 C++11 une expansion de pack pouvait survenir dans un initialiseur de membre non autorisé
CWG 2717 C++11 les instanciations de spécificateurs d'alignement étaient séparées par des virgules elles sont séparées par des espaces

Voir aussi

Function template Définit une famille de fonctions
Class template Définit une famille de classes
sizeof... Interroge le nombre d'éléments dans un pack
C-style variadic function Accepte un nombre variable d'arguments
Preprocessor macros Peut également être variadique
Fold expression Réduit un pack avec un opérateur binaire
Pack indexing Accède à l'élément d'un pack à un index spécifié