Namensräume
Varianten
Aktionen

Pack (seit C++11)

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Schablonenspezialisierung
Parameter-Packs (C++11)
Sonstiges
 
 
 
 

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

  • einen Parameter-Pack
  • ein Template-Parameter-Pack
  • einen Funktionsparameter-Pack
(seit C++20)
(seit C++26)

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

Ein Lambda-Init-Capture-Pack ist ein Lambda-Capture, der für jedes Element der Pack-Expansion seines Initialisierers einen Init-Capture einführt.

(seit C++20)

Ein strukturiertes Bindungs-Pack ist ein Bezeichner in der Deklaration einer strukturierten Bindung, der null oder mehr strukturierte Bindungen einführt.

(seit C++26)

Die Anzahl der Elemente eines Packs ist gleich

  • der Anzahl der für den Parameter-Pack bereitgestellten Argumente, wenn der Pack ein Template- oder Funktionsparameter-Pack ist,
  • der Anzahl der Elemente in der Pack-Expansion seines Initialisierers, wenn der Pack ein Lambda-Init-Capture-Pack ist,
(seit C++20)
  • der strukturierten Bindungsgröße des Initialisierers abzüglich der Anzahl der Nicht-Pack-Elemente in der Deklaration der strukturierten Bindung, wenn der Pack ein strukturiertes Bindungs-Pack ist.
(seit C++26)

Ein Template mit mindestens einem Parameter-Pack wird als variadic template (variadisches Template) bezeichnet.

Inhalt

[edit] Syntax

Template-Parameter-Pack (erscheint in Alias-Templates, Klassen-Templates, Variablen-Templates(seit C++14), Concepts(seit C++20) und Funktions-Templates-Parameterlisten)

type ... pack-name (optional) (1)
typename|class ... pack-name (optional) (2)
type-constraint ... pack-name (optional) (3) (seit C++20)
template < parameter-list > class ... pack-name (optional) (4) (bis C++17)
template < parameter-list > typename|class ... pack-name (optional) (4) (seit C++17)

Funktionsparameter-Pack (eine Form eines Deklarators, erscheint in der Funktionsparameterliste eines variadischen Funktions-Templates)

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

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

(seit C++20)

Pack-Expansion (erscheint im Körper eines Templates)

pattern ... (6)
1) Ein Nicht-Typ-Template-Parameter-Pack mit optionalem Namen
2) Ein Typ-Template-Parameter-Pack mit optionalem Namen
3) Ein eingeschränkter Typ-Template-Parameter-Pack mit optionalem Namen
(seit C++20)
4) Ein Template-Template-Parameter-Pack mit optionalem Namen
5) Ein Funktionsparameter-Pack mit optionalem Namen
6) Pack-Expansion: expandiert zu einer Liste von null oder mehr patterns. Das Muster muss mindestens einen Pack enthalten.

[edit] Erklärung

Ein variadisches Klassen-Template kann mit einer beliebigen Anzahl von Template-Argumenten instanziiert werden

template<class... Types>
struct Tuple {};
 
Tuple<> t0;           // Types contains no arguments
Tuple<int> t1;        // Types contains one argument: int
Tuple<int, float> t2; // Types contains two arguments: int and float
Tuple<0> t3;          // error: 0 is not a type

Ein variadisches Funktions-Template kann mit einer beliebigen Anzahl von Funktionsargumenten aufgerufen werden (die Template-Argumente werden durch Template-Argument-Deduction abgeleitet)

template<class... Types>
void f(Types... args);
 
f();       // OK: args contains no arguments
f(1);      // OK: args contains one argument: int
f(2, 1.0); // OK: args contains two arguments: int and double

In einem primären Klassen-Template muss der Template-Parameter-Pack der letzte Parameter in der Template-Parameterliste sein. In einem Funktions-Template darf der Template-Parameter-Pack früher in der Liste erscheinen, vorausgesetzt, alle nachfolgenden Parameter können aus den Funktionsargumenten abgeleitet werden oder haben Standardargumente.

template<typename U, typename... Ts>    // OK: can deduce U
struct valid;
// template<typename... Ts, typename U> // Error: Ts... not at the end
// struct Invalid;
 
template<typename... Ts, typename U, typename=void>
void valid(U, Ts...);    // OK: can deduce U
// void valid(Ts..., U); // Can't be used: Ts... is a non-deduced context in this position
 
valid(1.0, 1, 2, 3);     // OK: deduces U as double, Ts as {int, int, int}

Wenn jede gültige Spezialisierung eines variadischen Templates einen leeren Template-Parameter-Pack erfordert, ist das Programm fehlerhaft, keine Diagnose erforderlich.

[edit] Pack-Expansion

Ein Muster gefolgt von einer Ellipse, in dem der Name mindestens eines Packs mindestens einmal vorkommt, wird in null oder mehr Instanziierungen des Musters expandiert, wobei der Name des Packs durch jedes der Elemente aus dem Pack in Reihenfolge ersetzt wird. Instanziierungen von Ausrichtungs-Spezifizierern sind durch Leerzeichen getrennt, andere Instanziierungen sind durch Kommas getrennt.

template<class... Us>
void f(Us... pargs) {}
 
template<class... Ts>
void g(Ts... args)
{
    f(&args...); // “&args...” is a pack expansion
                 // “&args” is its pattern
}
 
g(1, 0.2, "a"); // Ts... args expand to int E1, double E2, const char* E3
                // &args... expands to &E1, &E2, &E3
                // Us... pargs expand to int* E1, double* E2, const char** E3

Wenn die Namen zweier Packs im selben Muster vorkommen, werden sie gleichzeitig expandiert und müssen die gleiche 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>... is the pack expansion
        // Pair<Args1, Args2> is the pattern
    };
};
 
typedef zip<short, int>::with<unsigned short, unsigned>::type T1;
// Pair<Args1, Args2>... expands to
// Pair<short, unsigned short>, Pair<int, unsigned int> 
// T1 is Tuple<Pair<short, unsigned short>, Pair<int, unsigned>>
 
// typedef zip<short>::with<unsigned short, unsigned>::type T2;
// error: pack expansion contains packs of different lengths

Wenn eine Pack-Expansion innerhalb einer anderen Pack-Expansion verschachtelt ist, werden die Packs, die innerhalb der innersten Pack-Expansion vorkommen, von dieser expandiert, und es muss ein weiterer Pack in der umschließenden Pack-Expansion erwähnt werden, aber nicht in der innersten.

template<class... Args>
void g(Args... args)
{
    f(const_cast<const Args*>(&args)...); 
    // const_cast<const Args*>(&args) is the pattern, it expands two packs
    // (Args and args) simultaneously
 
    f(h(args...) + args...); // Nested pack expansion:
    // inner pack expansion is "args...", it is expanded first
    // outer pack expansion is h(E1, E2, E3) + args..., it is expanded
    // second (as 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 (leerer Pack), ändert die Instanziierung einer Pack-Expansion die syntaktische Interpretation des umschließenden Konstrukts nicht, selbst in Fällen, in denen das Weglassen der Pack-Expansion ansonsten fehlerhaft wäre oder zu einer syntaktischen 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<> has no base classes
                     // x is a variable of type X<> that is value-initialized

[edit] Expansionsorte

Je nachdem, wo die Expansion stattfindet, ist die resultierende durch Kommas getrennte (oder für Ausrichtungs-Spezifizierer durch Leerzeichen getrennte) Liste eine andere Art von Liste: Funktionsparameterliste, Member-Initialisiererliste, Attributliste usw. Die folgende Liste zeigt alle zulässigen Kontexte.

[edit] Funktionsargumentlisten

Eine Pack-Expansion kann innerhalb der Klammern eines Funktionsaufrufoperators erscheinen, in diesem Fall ist der größte Ausdruck oder die Klammer-eingeschlossene Initialisiererliste links von der Ellipse das Muster, das expandiert wird.

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

[edit] Klammer-initialisierte Initialisierer

Eine Pack-Expansion kann innerhalb der Klammern eines direkten Initialisierers, eines Funktionsstil-Casts und anderer Kontexte (Member-Initialisierer, new-Ausdruck usw.) erscheinen, in diesem Fall sind die Regeln identisch mit den Regeln für einen Funktionsaufrufausdruck.

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

[edit] Klammer-eingeschlossene Initialisierer

In einer Klammer-eingeschlossenen Initialisiererliste kann auch eine Pack-Expansion erscheinen.

template<typename... Ts>
void func(Ts... args)
{
    const int size = sizeof...(args) + 2;
    int res[size] = {1, args..., 2};
 
    // since initializer lists guarantee sequencing, this can be used to
    // call a function on each element of a pack, in order:
    int dummy[sizeof...(Ts)] = {(std::cout << args, 0)...};
}

[edit] Template-Argumentlisten

Pack-Expansionen können überall in einer Template-Argumentliste verwendet werden, vorausgesetzt, das Template hat die Parameter, die zur Erweiterung passen.

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

[edit] Funktionsparameterliste

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

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

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

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

[edit] Template-Parameterliste

Pack-Expansion kann in einer Template-Parameterliste erscheinen.

template<typename... T>
struct value_holder
{
    template<T... Values> // expands to a non-type template parameter 
    struct apply {};      // list, such as <int, char, int(&)[5]>
};

[edit] Basis-Spezifizierer und Member-Initialisiererlisten

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

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

[edit] Lambda-Captures

Pack-Expansion kann in der Capture-Klausel eines Lambda-Ausdrucks erscheinen.

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

[edit] 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 Ausnahme-Spezifikationen

Die Liste der Ausnahmen in einer dynamischen Ausnahme-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)

[edit] Ausrichtungs-Spezifizierer

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

template<class... T>
struct Align
{
    alignas(T...) unsigned char buffer[128];
};
 
Align<int, short> a; // the alignment specifiers after expansion are
                     // alignas(int) alignas(short)
                     // (no comma in between)

[edit] Attributliste

Pack-Expansionen sind in den Listen von Attributen erlaubt, sofern die Spezifikation des Attributs dies zulässt. Zum Beispiel:

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

Falt-Ausdrücke

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

Using-Deklarationen

In Using-Deklarationen kann eine Ellipse in der Liste der Deklaratoren erscheinen. Dies ist nützlich, wenn von einem Template-Parameter-Pack abgeleitet wird.

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


Paketindizierung

In der Pack-Indizierung enthält die Pack-Expansion einen unexpandierten Pack, gefolgt von einer Ellipse und einem Index. Das Muster eines Pack-Indizierungs-Ausdrucks ist ein identifier, während das Muster eines Pack-Indizierungs-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 mit 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;           // 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>>

Fold-erweiterte Constraints

In Fold-erweiterten Constraints ist das Muster der Constraint dieses Fold-erweiterten Constraints.

Ein Fold-erweiterter Constraint wird nicht instanziiert.

(seit C++26)

[edit] Anmerkungen

Feature-Test-Makro Wert Std Feature
__cpp_variadic_templates 200704L (C++11) Variadische Templates
__cpp_pack_indexing 202311L (C++26) Paketindizierung

[edit] Beispiel

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

Die erste Überladung wird aufgerufen, wenn nur der Formatstring übergeben wird und keine Parametererweiterung stattfindet.

Die zweite Überladung enthält einen separaten Template-Parameter für den Kopf der Argumente und einen Parameter-Pack. Dies ermöglicht dem rekursiven Aufruf, nur den Schwanz 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) // base function
{
    std::cout << format;
}
 
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function
{
    for (; *format != '\0'; format++)
    {
        if (*format == '%')
        {
            std::cout << value;
            tprintf(format + 1, Fargs...); // recursive call
            return;
        }
        std::cout << *format;
    }
}
 
int main()
{
    tprintf("% world% %\n", "Hello", '!', 123);
}

Ausgabe

Hello world! 123

[edit] Fehlerberichte

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

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 1533 C++11 eine Pack-Expansion könnte in einem Member-Initialisierer für ein Mitglied auftreten nicht erlaubt
CWG 2717 C++11 Instanziierungen von Ausrichtungs-Spezifizierern waren durch Kommas getrennt sie sind durch Leerzeichen getrennt

[edit] Siehe auch

Funktionsschablone Definiert eine Familie von Funktionen
Klassenschablone Definiert eine Familie von Klassen
sizeof... Fragt die Anzahl der Elemente in einem Pack ab
C-style variadic function (C-artiges variadisches Funktion) Nimmt eine variable Anzahl von Argumenten entgegen
Präprozessor-Makros Können ebenfalls variadisch sein
Fold-Ausdruck Reduziert einen Pack über einen binären Operator
Paketindizierung Greift auf das Element eines Packs am angegebenen Index zu