Namespaces
Variants

Pack indexing (since C++26)

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
Miscellaneous

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

Inhaltsverzeichnis

Syntax

id-expression ...[ expression ] (1)
typedef-name ...[ expression ] (2)
1) Pack-Indexierungsausdruck
2) Pack-Indexierungs-Spezifizierer
typedef-name - ein Identifier oder eine simple-template-id , die einen Pack benennt
id-expression - eine id-expression , die einen Pack benennt
expression - ein converted constant expression I vom Typ std:: size_t , der als Index festgelegt ist, wobei I innerhalb des Bereichs [ 0 , sizeof... ( P ) ) für einen Pack P in der Pack-Indizierung liegt

Erklärung

Pack-Indexing ist eine Pack-Expansion des nicht expandierten Packs, gefolgt von einer Ellipse und einem Index innerhalb des Subscripts. Es gibt zwei Arten von Pack-Indexing: Pack-Indexing-Ausdruck und Pack-Indexing-Spezifizierer.

Sei P ein nicht-leerer Pack, der P 0 , P 1 , ..., P n-1 enthält, und I ein gültiger Index. Die Instanziierung der Expansion P...[I] ergibt das Pack-Element P I von P .

Das Indizieren eines Packs mit einem nicht-konstanten Ausdrucksindex 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]; // Fehler: 'm' ist kein konstanter Ausdruck
    auto d = args...[runtime_idx()]; // Fehler: 'runtime_idx()' ist kein konstanter Ausdruck
}

Das Indizieren eines Packs von Template-Template-Parametern ist nicht möglich.

template <template <typename...> typename... Temps>
using A = Temps...[0]<>; // Fehler: 'Temps' ist ein Pack von Template-Template-Parametern
template <template <typename...> typename... Temps>
using B = Temps<>...[0]; // Fehler: 'Temps<>' bezeichnet keinen Pack-Namen
                         // obwohl es sich um eine Simple-Template-ID handelt

Pack-Indexierungsausdruck

id-expression ...[ expression ]

Pack-Indexierungsausdruck bezeichnet den id-expression , den Ausdruck des Pack-Elements P I . Der id-expression muss durch die Deklaration von eingeführt werden:

template <std::size_t I, typename... Ts>
constexpr auto element_at(Ts... args)
{
    // 'args' in Funktionsparameter-Pack-Deklaration eingeführt
    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); // Fehler: Außerhalb der Grenzen
static_assert(element_at<0>() == 1); // Fehler: Außerhalb der Grenzen, leeres Pack
template <std::size_t I, typename Tup>
constexpr auto structured_binding_element_at(Tup tup)
{
    auto [...elems] = tup;
    // 'elems' in strukturierter Bindungs-Pack-Deklaration eingeführt
    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' in konstanter Template-Parameter-Pack-Deklaration eingeführt
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' in Lambda-Init-Capture-Pack eingeführt
        return members...[I] + op;
    };
}
static_assert(foo<0>(4, "Hello", true)(5) == 9);
static_assert(foo<1>(3, std::string("C++"))("26") == "C++26");

Die Indizierung von komplexen Ausdrücken außer Id-Ausdrücken ist nicht erlaubt.

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

Die Anwendung von decltype auf Pack-Indexierungsausdrücke ist identisch mit der Anwendung von decltype auf Id-Ausdrücke.

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

Pack-Indexierungs-Spezifizierer

Typname ...[ Ausdruck ]

Der Pack-Indexierungs-Spezifizierer bezeichnet den computed-type-specifier , den Typ des Pack-Elements P I . Der typedef-name muss durch die Deklaration eines type template parameter pack eingeführt werden.

template <typename... Ts>
using last_type_t = Ts...[sizeof...(Ts) - 1];
static_assert(std::is_same_v<last_type_t<>, int>); // Fehler: Außerhalb der Grenzen
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-Indexierungsspezifizierer kann auftreten als:

Der Pack-Indexierungs-Spezifizierer kann in Funktions- oder Konstruktorparameterlisten verwendet werden, um nicht abgeleitete Kontexte in der Template-Argumentableitung zu etablieren.

template <typename...>
struct type_seq {};
template <typename... Ts>
auto f(Ts...[0] arg, type_seq<Ts...>)
{
    return arg;
}
// OK: "Hello" wird implizit zu 'std::string_view' konvertiert
std::same_as<std::string_view> auto a = f("Hello", type_seq<std::string_view>{});
// Fehler: "Ok" ist nicht zu 'int' konvertierbar
std::same_as<int> auto b = f("Ok", type_seq<int, const char*>{});

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 anschließend zu Zeigern angepasst wurden. Seit C++26 wird Ts... [ 1 ] als Pack-Index-Spezifizierer interpretiert, was das nachfolgende Verhalten zu #2 ändern würde. Um das erste Verhalten beizubehalten, muss das Funktionsparameter-Pack benannt oder manuell zu einem 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*...); // klarer, aber permissiver: Ts... kann cv void oder Funktionstypen enthalten
void foo() 
{
    f<char, bool>(nullptr, nullptr);
    // Verhalten #1 (vor C++26):
    //  ruft 'f<char, bool>(char*, bool*)' auf (alias 'f<char, bool>(char[1], bool[1])')
    // Verhalten #2 (seit C++26): 
    //  Fehler: vermeintlich 'void f<char, bool>(bool)' aufgerufen
    //  aber mit 2 Argumenten statt 1 bereitgestellt
    g<char, bool>(nullptr, nullptr);
    // ruft 'g<char, bool>(char*, bool*)' auf (alias 'g<char, bool>(char[1], bool[1])')
    h<char, bool>(nullptr, nullptr);
    // ruft 'h<char, bool>(char*, bool*)' auf
}
Feature-Test-Makro Wert Std Feature
__cpp_pack_indexing 202311L (C++26) Pack-Indexierung

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