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

Ein Pack ist eine C++-Entität, die eines der folgenden Elemente definiert:

  • ein Parameterpaket
  • Template-Parameterpaket
  • Funktionsparameterpaket
(seit C++20)
(seit C++26)

Ein Template-Parameter-Pack ist ein Template-Parameter, der null oder mehr Template-Argumente (Konstanten, Typen oder Templates) akzeptiert. Ein Funktionsparameter-Pack ist ein Funktionsparameter, der null oder mehr Funktionsargumente akzeptiert.

Ein Lambda-Init-Capture-Pack ist eine Lambda-Capture, die für jedes Element in der Pack-Expansion ihres Initialisierers eine Init-Capture einführt.

(since C++20)

Ein strukturierter Bindungs-Pack ist ein Bezeichner in der strukturierten Bindungsdeklaration, der null oder mehr strukturierte Bindungen einführt.

(since C++26)

Die Anzahl der Elemente eines Packs ist gleich:

  • die Anzahl der für den Parameter-Pack bereitgestellten Argumente, falls es sich um einen Template- oder Funktionsparameter-Pack handelt,
  • die Anzahl der Elemente in der Paketentwicklung seines Initialisierers, wenn das Paket ein Lambda-Init-Capture-Paket ist,
(seit C++20)
  • Größe der strukturierten Bindung des Initialisierers abzüglich der Anzahl der Nicht-Pack-Elemente in der strukturierten Bindungsdeklaration, falls es sich bei dem Pack um ein strukturiertes Bindungs-Pack handelt.
(seit C++26)

Eine Vorlage mit mindestens einem Parameterpaket wird als variadic template bezeichnet.

Inhaltsverzeichnis

Syntax

Template-Parameter-Paket (erscheint in Alias-Template , Klassentemplate , Variablen-Template (seit C++14) , Concept (seit C++20) und Funktionstemplate Parameterlisten)

Typ ... Paketname  (optional) (1)
typename | class ... Paketname  (optional) (2)
Typ-Einschränkung ... Paketname  (optional) (3) (seit C++20)
template < Parameterliste > class ... Paketname  (optional) (4) (bis C++17)
template < Parameterliste > typename | class ... Paketname  (optional) (4) (seit C++17)

Funktionsparameter-Pack (eine Form des Deklarators , erscheint in einer Funktionsparameterliste einer variadischen Funktionsvorlage)

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

Für die Syntax von Nicht-Parameter-Packs, siehe Lambda-Init-Capture-Pack und Structured-Binding-Pack (seit C++26) .

(seit C++20)

Pack-Erweiterung (erscheint im Rumpf eines Templates)

pattern ... (6)
1) Ein konstanter Template-Parameter-Pack mit einem optionalen Namen
2) Ein Typ-Template-Parameterpaket mit einem optionalen Namen
3) Ein constrained Typ-Template-Parameterpaket mit einem optionalen Namen
(since C++20)
4) Ein Template-Template-Parameterpaket mit einem optionalen Namen
5) Ein Funktionsparameter-Pack mit einem optionalen Namen
6) Pack-Expansion: erweitert sich zu einer Liste von null oder mehr pattern s. Das Pattern muss mindestens einen Pack enthalten.

Erklärung

Ein variadisches Klassentemplate kann mit beliebig vielen Template-Argumenten instanziiert werden:

template<class... Types>
struct Tuple {};
Tuple<> t0;           // Types enthält keine Argumente
Tuple<int> t1;        // Types enthält ein Argument: int
Tuple<int, float> t2; // Types enthält zwei Argumente: int und float
Tuple<0> t3;          // Fehler: 0 ist kein Typ

Eine variadische Funktionsvorlage kann mit beliebig vielen Funktionsargumenten aufgerufen werden (die Vorlagenargumente werden durch Template-Argument-Deduktion abgeleitet):

template<class... Types>
void f(Types... args);
f();       // OK: args enthält keine Argumente
f(1);      // OK: args enthält ein Argument: int
f(2, 1.0); // OK: args enthält zwei Argumente: int und double

In einer primären Klassenvorlage muss der Template-Parameterpack der letzte Parameter in der Template-Parameterliste sein. In einer Funktionsvorlage kann der Template-Parameterpack früher in der Liste erscheinen, vorausgesetzt, dass alle folgenden Parameter von den Funktionsargumenten abgeleitet werden können oder Standardargumente haben:

template<typename U, typename... Ts>    // OK: kann U ableiten
struct valid;
// template<typename... Ts, typename U> // Fehler: Ts... nicht am Ende
// struct Invalid;
template<typename... Ts, typename U, typename=void>
void valid(U, Ts...);    // OK: kann U ableiten
// void valid(Ts..., U); // Kann nicht verwendet werden: Ts... ist in dieser Position ein nicht ableitbarer Kontext
valid(1.0, 1, 2, 3);     // OK: leitet U als double ab, Ts als {int, int, int}

Wenn jede gültige Spezialisierung eines variadischen Templates ein leeres Template-Parameterpaket erfordert, ist das Programm ill-formed, keine Diagnose erforderlich.

Paketerweiterung

Einem Muster, dem eine Auslassung folgt, in dem der Name mindestens eines Packs mindestens einmal erscheint, wird erweitert in null oder mehr Instanzen des Musters, wobei der Name des Packs durch jedes der Elemente aus dem Pack in der Reihenfolge ersetzt wird. Instanzen von alignment specifiers werden durch Leerzeichen getrennt, andere Instanzen werden durch Kommas getrennt.

template<class... Us>
void f(Us... pargs) {}
template<class... Ts>
void g(Ts... args)
{
    f(&args...); // „&args...“ ist eine Pack-Erweiterung
                 // „&args“ ist ihr Muster
}
g(1, 0.2, "a"); // Ts... args expandiert zu int E1, double E2, const char* E3
                // &args... expandiert zu &E1, &E2, &E3
                // Us... pargs expandiert zu int* E1, double* E2, const char** E3

Wenn die Namen zweier Pakete im selben Muster erscheinen, werden sie gleichzeitig erweitert und müssen dieselbe Länge haben:

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>... ist die Paketentfaltung
        // Pair<Args1, Args2> ist das Muster
    };
};
typedef zip<short, int>::with<unsigned short, unsigned>::type T1;
// Pair<Args1, Args2>... entfaltet sich zu
// Pair<short, unsigned short>, Pair<int, unsigned int> 
// T1 ist Tuple<Pair<short, unsigned short>, Pair<int, unsigned>>
// typedef zip<short>::with<unsigned short, unsigned>::type T2;
// Fehler: Paketentfaltung enthält Pakete unterschiedlicher Längen

Wenn eine Paketentfaltung innerhalb einer anderen Paketentfaltung verschachtelt ist, werden die Pakete, die in der innersten Paketentfaltung erscheinen, von dieser entfaltet, und es muss ein weiteres Paket in der umschließenden Paketentfaltung erwähnt werden, jedoch nicht in der innersten:

template<class... Args>
void g(Args... args)
{
    f(const_cast<const Args*>(&args)...); 
    // const_cast<const Args*>(&args) ist das Muster, es erweitert zwei Packs
    // (Args und args) gleichzeitig
    f(h(args...) + args...); // Verschachtelte Pack-Erweiterung:
    // innere Pack-Erweiterung ist "args...", sie wird zuerst erweitert
    // äußere Pack-Erweiterung ist h(E1, E2, E3) + args..., sie wird
    // danach erweitert (als h(E1, E2, E3) + E1, h(E1, E2, E3) + E2, h(E1, E2, E3) + E3)
}

Wenn die Anzahl der Elemente in einem Pack null ist (leeres Pack), ändert die Instanziierung einer Pack-Expansion die syntaktische Interpretation des umschließenden Konstrukts nicht, selbst in Fällen, in denen das vollständige Weglassen der Pack-Expansion andernfalls fehlerhaft wäre oder zu einer Syntax-Mehrdeutigkeit führen würde. Die Instanziierung erzeugt eine leere Liste.

template<class... Bases> 
struct X : Bases... { };
template<class... Args> 
void f(Args... args) 
{
    X<Args...> x(args...);
}
template void f<>(); // OK, X<> hat keine Basisklassen
                     // x ist eine Variable vom Typ X<>, die wertinitialisiert wird

Expansionsorte

Je nachdem, wo die Erweiterung stattfindet, ist die resultierende durch Kommas getrennte (oder für Ausrichtungsbezeichner durch Leerzeichen getrennte) Liste eine unterschiedliche Art von Liste: Funktionsparameterliste, Member-Initialisierungsliste, Attributliste usw. Folgendes ist die Liste aller zulässigen Kontexte:

Funktionsargumentlisten

Eine Paketentfaltung kann innerhalb der Klammern eines Funktionsaufrufoperators erscheinen, wobei der größte Ausdruck oder die geschweifte Initialisierungsliste links von den Auslassungspunkten das Muster ist, das entfaltet wird:

f(args...);              // expandiert zu f(E1, E2, E3)
f(&args...);             // expandiert zu f(&E1, &E2, &E3)
f(n, ++args...);         // expandiert zu f(n, ++E1, ++E2, ++E3);
f(++args..., n);         // expandiert zu 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...); // expandiert zu 
// f(h(E1, E2, E3) + E1, h(E1, E2, E3) + E2, h(E1, E2, E3) + E3)

Initialisierungen in Klammern

Eine Paketentfaltung kann innerhalb der Klammern eines direkten Initialisierers , eines funktionsstil-Casts und in anderen Kontexten auftreten ( Member-Initialisierer , new-Ausdruck , etc.), wobei die Regeln identisch zu den Regeln für einen Funktionsaufrufausdruck oben sind:

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

Geschweifte Klammern-Initialisierer

In einer von geschweiften Klammern umschlossenen Initialisierungsliste kann ebenfalls eine Paketentwicklung auftreten:

template<typename... Ts>
void func(Ts... args)
{
    const int size = sizeof...(args) + 2;
    int res[size] = {1, args..., 2};
    // Da Initialisierungslisten eine Reihenfolge garantieren, kann dies verwendet werden,
    // um eine Funktion für jedes Element eines Packs in Reihenfolge aufzurufen:
    int dummy[sizeof...(Ts)] = {(std::cout << args, 0)...};
}

Template-Argumentenlisten

Pack-Erweiterungen können überall in einer Template-Argumentliste verwendet werden, sofern das Template über die entsprechenden Parameter verfügt, um die Erweiterung aufzunehmen:

template<class A, class B, class... C>
void func(A arg1, B arg2, C... arg3)
{
    container<A, B, C...> t1; // erweitert zu container<A, B, E1, E2, E3> 
    container<C..., A, B> t2; // erweitert zu container<E1, E2, E3, A, B> 
    container<A, C..., B> t3; // erweitert zu container<A, E1, E2, E3, B> 
}

Funktionsparameterliste

In einer Funktionsparameterliste, wenn eine Ellipse in einer Parameterdeklaration erscheint (ob sie einen Funktionsparameterpack benennt (wie in, Args ... args ) oder nicht) ist die Parameterdeklaration das Muster:

template<typename... Ts>
void f(Ts...) {}
f('a', 1); // Ts... expandiert zu void f(char, int)
f(0.1);    // Ts... expandiert zu 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] expandiert zu 
                            // const char (&)[2], int(&)[1]

Hinweis: Im Muster Ts (&...arr)[N] ist die Ellipse das innerste Element, nicht das letzte Element wie bei allen anderen Pack-Erweiterungen.

Hinweis: Ts (&...)[N] ist nicht erlaubt, da die C++11-Grammatik erfordert, dass die eingeklammerte Ellipse einen Namen hat: CWG issue 1488 .

Template-Parameterliste

Paketentfaltung kann in einer Template-Parameterliste erscheinen:

template<typename... T>
struct value_holder
{
    template<T... Values> // erweitert sich zu einer konstanten Template-Parameter
    struct apply {};      // Liste, wie z.B. <int, char, int(&)[5]>
};

Basis-Spezifizierer und Member-Initialisierungslisten

Eine Pack-Erweiterung kann die Liste der Basisklassen in einer Klassendeklaration bezeichnen. Typischerweise bedeutet dies auch, dass der Konstruktor eine Pack-Erweiterung in der Member-Initialisierungsliste verwenden muss, um die Konstruktoren dieser Basen aufzurufen:

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

Lambda-Ausdrücke Erfassungen

Paketentfaltung kann in der Erfassungsklausel eines Lambda -Ausdrucks erscheinen:

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

Der sizeof... Operator

Der sizeof... Operator wird ebenfalls als Pack-Expansion klassifiziert:

template<class... Types>
struct count
{
    static const std::size_t value = sizeof...(Types);
};

Dynamische Exception-Spezifikationen

Die Liste der Exceptions in einer dynamischen Exception-Spezifikation kann ebenfalls eine Pack-Expansion sein:

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

Ausrichtungsspezifizierer

Pack-Erweiterungen sind sowohl in den Typenlisten als auch in den Ausdruckslisten erlaubt, die vom Schlüsselwort alignas verwendet werden. Die Instanziierungen werden durch Leerzeichen getrennt:

template<class... T>
struct Align
{
    alignas(T...) unsigned char buffer[128];
};
Align<int, short> a; // die Ausrichtungsspezifizierer nach der Erweiterung sind
                     // alignas(int) alignas(short)
                     // (kein Komma dazwischen)

Attributliste

Pack-Erweiterungen sind in den Listen von Attributen erlaubt, sofern durch die Spezifikation des Attributs zugelassen. Zum Beispiel:

template<int... args>
[[vendor::attr(args)...]] void* f();

Fold-Ausdrücke

In Fold-Ausdrücken ist das Muster der gesamte Teilausdruck, der kein unentpacktes Pack enthält.

Using-Deklarationen

In Using-Deklarationen können Auslassungspunkte in der Liste der Deklaratoren erscheinen, dies ist nützlich bei Ableitung von einem Template-Parameter-Pack:

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


Pack-Indexierung

Bei der Pack-Indexierung enthält die Pack-Erweiterung ein nicht expandiertes Pack gefolgt von einer Ellipse und einem Index. Das Muster des Pack-Indexierungsausdrucks ist ein Bezeichner , während das Muster des Pack-Indexierungs-Spezifizierers ein Typedef-Name ist.

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

Friend-Deklarationen

In Klassen- Friend-Deklarationen kann jeder Typspezifizierer von einer Ellipse gefolgt werden:

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;           // Klassen C und E sind Friends von R<C, E>
R<R<E>, R<C, int>> rr; // E::Nested und C sind Friends von R<R<E>, R<C, int>>

Falt-erweiterte Constraints

Bei falt-erweiterten Constraints ist das Muster das Constraint dieses falt-erweiterten Constraints.

Ein falt-erweitertes Constraint wird nicht instanziiert.

(seit C++26)

Hinweise

Feature-Test Makro Wert Std Feature
__cpp_variadic_templates 200704L (C++11) Variadic templates
__cpp_pack_indexing 202311L (C++26) Pack indexing

Beispiel

Das folgende Beispiel definiert eine Funktion ähnlich std::printf , die jedes Vorkommen des Zeichens % im Formatstring durch einen Wert ersetzt.

Die erste Überladung wird aufgerufen, wenn nur die Formatzeichenkette übergeben wird und keine Parameterersetzung stattfindet.

Die zweite Überladung enthält einen separaten Template-Parameter für den Kopf der Argumente und einen Parameter-Pack, dies ermöglicht den rekursiven Aufruf, nur den Rest der Parameter zu übergeben, bis er leer wird.

Targs ist der Template-Parameter-Pack und Fargs ist der Funktionsparameter-Pack.

#include <iostream>
void tprintf(const char* format) // Basisfunktion
{
    std::cout << format;
}
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // rekursive variadische Funktion
{
    for (; *format != '\0'; format++)
    {
        if (*format == '%')
        {
            std::cout << value;
            tprintf(format + 1, Fargs...); // rekursiver Aufruf
            return;
        }
        std::cout << *format;
    }
}
int main()
{
    tprintf("% world% %\n", "Hello", '!', 123);
}

Ausgabe:

Hello world! 123

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrektes Verhalten
CWG 1533 C++11 eine Pack-Expansion konnte in einem Member-Initialisierer für ein Member auftreten nicht erlaubt
CWG 2717 C++11 Instanziierungen von Ausrichtungs-Spezifizierern waren kommagetrennt sie sind leerzeichengetrennt

Siehe auch

Function template Definiert eine Familie von Funktionen
Class template Definiert eine Familie von Klassen
sizeof... Ermittelt die Anzahl der Elemente in einem Pack
C-style variadic function Akzeptiert eine variable Anzahl von Argumenten
Preprocessor macros Können ebenfalls variadisch sein
Fold expression Reduziert ein Pack über einen binären Operator
Pack indexing Greift auf das Element eines Packs an einem bestimmten Index zu