Namensräume
Varianten
Aktionen

Pack-Indizierung (seit C++26)

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
Sonstiges
 
 
 
 

Greift auf das Element eines Packs an einem bestimmten Index zu.

Inhalt

[bearbeiten] Syntax

id-Ausdruck ...[ Ausdruck ] (1)
typedef-Name ...[ Ausdruck ] (2)
1) Pack-Indizierungs-Ausdruck
2) Pack-Indizierungs-Spezifizierer
typedef-Name - ein Bezeichner oder eine einfache Template-ID, die einen Pack benennt
id-Ausdruck - ein id-Ausdruck, der einen Pack benennt
expression - ein konvertierter konstanter Ausdruck I vom Typ std::size_t, bezeichnet als Index, wobei I im Bereich [0sizeof...(P)) für irgendeinen Pack P in der Pack-Indizierung liegt

[bearbeiten] Erklärung

Pack-Indizierung ist eine Pack-Erweiterung des nicht erweiterten Packs gefolgt von einer Ellipse und einem Index innerhalb der Klammern. Es gibt zwei Arten der Pack-Indizierung: Pack-Indizierungs-Ausdruck und Pack-Indizierungs-Spezifizierer.

Sei P ein nicht-leerer Pack, der P0, P1, ..., Pn-1 enthält, und sei I ein gültiger Index, so ergibt die Instanziierung der Erweiterung P...[I] das Pack-Element PI von P.

Die Indizierung eines Packs mit einem nicht-konstanten Ausdruck als Index I ist nicht erlaubt.

int runtime_idx();
 
void bar(auto... args)
{
    auto a = args...[0];
    const int n = 1;
    auto b = args...[n];
    int m = 2;
    auto c = args...[m]; // error: 'm' is not a constant expression
    auto d = args...[runtime_idx()]; // error: 'runtime_idx()' is not a constant expression
}

Die Indizierung eines Packs von Template-Template-Parametern ist nicht möglich.

template <template <typename...> typename... Temps>
using A = Temps...[0]<>; // error: 'Temps' is a pack of template template parameters
 
template <template <typename...> typename... Temps>
using B = Temps<>...[0]; // error: 'Temps<>' doesn't denote pack name 
                         // although it is a simple-template-id

[bearbeiten] Pack-Indizierungs-Ausdruck

id-Ausdruck ...[ Ausdruck ]

Der Pack-Indizierungs-Ausdruck bezeichnet den id-Ausdruck, den Ausdruck des Pack-Elements PI. Der id-Ausdruck muss durch die Deklaration von

template <std::size_t I, typename... Ts>
constexpr auto element_at(Ts... args)
{
    // 'args' introduced in function parameter pack declaration
    return args...[I];
}
 
static_assert(element_at<0>(3, 5, 9) == 3);
static_assert(element_at<2>(3, 5, 9) == 9);
static_assert(element_at<3>(3, 5, 9) == 4); // error:  out of bounds
static_assert(element_at<0>() == 1); // error: out of bounds, empty pack
 
template <std::size_t I, typename Tup>
constexpr auto structured_binding_element_at(Tup tup)
{
    auto [...elems] = tup;
    // 'elems' introduced in structured binding pack declaration
    return elems...[I];
}
 
struct A { bool a; int b; };
 
static_assert(structured_binding_element_at<0>(A {true, 4}) == true);
static_assert(structured_binding_element_at<1>(A {true, 4}) == 4);
 
// 'Vals' introduced in non-type template parameter pack declaration
template <std::size_t I, std::size_t... Vals>
constexpr std::size_t double_at = Vals...[I] * 2; // OK
 
template <std::size_t I, typename... Args>
constexpr auto foo(Args... args)
{
    return [...members = args](Args...[I] op)
    {
        // 'members' introduced in lambda init-capture pack
        return members...[I] + op;
    };
}
 
static_assert(foo<0>(4, "Hello", true)(5) == 9);
static_assert(foo<1>(3, std::string("C++"))("26") == "C++26");

eingeführt werden. Die Indizierung von Packs mit komplexen Ausdrücken, die keine id-Ausdrücke sind, ist nicht erlaubt.

template <std::size_t I, auto... Vals>
constexpr auto identity_at = (Vals)...[I]; // error
// use 'Vals...[I]' instead
 
template <std::size_t I, std::size_t... Vals>
constexpr std::size_t triple_at = (Vals * 3)...[I]; // error
// use 'Vals...[I] * 3' instead
 
template <std::size_t I, typename... Args>
constexpr decltype(auto) get(Args&&... args) noexcept
{
    return std::forward<Args>(args)...[I]; // error
    // use 'std::forward<Args...[I]>(args...[I])' instead
}

Das Anwenden von decltype auf einen Pack-Indizierungs-Ausdruck ist dasselbe wie das Anwenden von decltype auf einen id-Ausdruck.

void f() 
{
    [](auto... args)
    {
        using T0 = decltype(args...[0]);   // 'T0' is 'double'
        using T1 = decltype((args...[0])); // 'T1' is 'double&'
    }(3.14);
}

[bearbeiten] Pack-Indizierungs-Spezifizierer

typedef-Name ...[ Ausdruck ]

Der Pack-Indizierungs-Spezifizierer bezeichnet den berechneten Typ-Spezifizierer, den Typ des Pack-Elements PI. Der typedef-Name muss durch die Deklaration eines Typ-Template-Parameter-Packs eingeführt werden.

template <typename... Ts>
using last_type_t = Ts...[sizeof...(Ts) - 1];
 
static_assert(std::is_same_v<last_type_t<>, int>); // error: out of bounds
static_assert(std::is_same_v<last_type_t<int>, int>);
static_assert(std::is_same_v<last_type_t<bool, char>, char>);
static_assert(std::is_same_v<last_type_t<float, int, bool*>, bool*>);

Der Pack-Indizierungs-Spezifizierer kann erscheinen als

Der Pack-Indizierungs-Spezifizierer kann in der Parameterliste einer Funktion oder eines Konstruktors verwendet werden, um nicht abgeleitete Kontexte bei der Template-Argumentenableitung zu etablieren.

template <typename...>
struct type_seq {};
 
template <typename... Ts>
auto f(Ts...[0] arg, type_seq<Ts...>)
{
    return arg;
}
 
// OK: "Hello" is implicitly converted to 'std::string_view'
std::same_as<std::string_view> auto a = f("Hello", type_seq<std::string_view>{});
 
// Error: "Ok" is not convertible to 'int'
std::same_as<int> auto b = f("Ok", type_seq<int, const char*>{});

[bearbeiten] Hinweise

Vor C++26 war Ts...[N] eine gültige Syntax zur Deklaration eines Funktionsparameter-Packs unbenannter Arrays der Größe N, wobei die Parametertypen weiter zu Zeigern angepasst wurden. Seit C++26 wird Ts...[1] als Pack-Indizierungs-Spezifizierer interpretiert, was das Verhalten unten zu #2 ändern würde. Um das erste Verhalten zu erhalten, muss das Funktionsparameter-Pack einen Namen haben oder manuell an ein Pack von Zeigertypen angepasst werden.

template <typename... Ts>
void f(Ts... [1]);
 
template <typename... Ts>
void g(Ts... args[1]);
 
template <typename... Ts>
void h(Ts*...); // clearer but more permissive: Ts... can contain cv void or function types
 
void foo() 
{
    f<char, bool>(nullptr, nullptr);
    // behavior #1 (before C++26):
    //  calls void 'f<char, bool>(char*, bool*)' (aka 'f<char, bool>(char[1], bool[1])')
    // behavior #2 (since C++26): 
    //  error: supposedly called 'void f<char, bool>(bool)'
    //  but provided with 2 arguments instead of 1
 
    g<char, bool>(nullptr, nullptr);
    // calls 'g<char, bool>(char*, bool*)' (aka 'g<char, bool>(char[1], bool[1])')
 
    h<char, bool>(nullptr, nullptr);
    // calls 'h<char, bool>(char*, bool*)'
}
Feature-Testmakro Wert Std Feature
__cpp_pack_indexing 202311L (C++26) Paketindizierung

[bearbeiten] Beispiel

#include <tuple>
 
template <std::size_t... Indices, typename Decomposable>
constexpr auto splice(Decomposable d)
{
    auto [...elems] = d;
    return std::make_tuple(elems...[Indices]...);
}
 
struct Point
{
    int x;
    int y;
    int z;
};
 
int main() 
{
    constexpr Point p { .x = 1, .y = 4, .z = 3 };
    static_assert(splice<2, 1, 0>(p) == std::make_tuple(3, 4, 1));
    static_assert(splice<1, 1, 0, 0>(p) == std::make_tuple(4, 4, 1, 1));
}