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

Accède à l'élément d'un pack à un index spécifié.

Table des matières

Syntaxe

expression d'identifiant ...[ expression ] (1)
nom de type défini ...[ expression ] (2)
1) Expression d'indexation de pack
2) Spécificateur d'indexation de paquet
typedef-name - un identifiant ou un simple-template-id qui désigne un pack
id-expression - une id-expression qui désigne un pack
expression - une expression constante convertie I de type std:: size_t désignée comme index où I est dans l'intervalle [ 0 , sizeof... ( P ) ) pour un pack P dans l'indexation de pack

Explication

L'indexation de paquet est une expansion de paquet du paquet non développé suivie d'une ellipse et d'un index à l'intérieur de l'indice. Il existe deux types d'indexation de paquet : l'expression d'indexation de paquet et le spécificateur d'indexation de paquet.

Soit P un pack non vide contenant P 0 , P 1 , ..., P n-1 et I un index valide, l'instanciation de l'expansion P...[I] produit l'élément du pack P I de P .

L'indexation d'un pack avec un index d'expression non constante I n'est pas autorisée.

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]; // erreur : 'm' n'est pas une expression constante
    auto d = args...[runtime_idx()]; // erreur : 'runtime_idx()' n'est pas une expression constante
}

L'indexation d'un pack de paramètres template template n'est pas possible.

template <template <typename...> typename... Temps>
using A = Temps...[0]<>; // erreur : 'Temps' est un pack de paramètres template template
template <template <typename...> typename... Temps>
using B = Temps<>...[0]; // erreur : 'Temps<>' ne dénote pas un nom de pack
                         // bien que ce soit un simple-template-id

Expression d'indexation de pack

id-expression ...[ expression ]

L'expression d'indexation de paquet désigne l' id-expression , l'expression de l'élément de paquet P I . L' id-expression doit être introduite par la déclaration de :

template <std::size_t I, typename... Ts>
constexpr auto element_at(Ts... args)
{
    // 'args' introduit dans la déclaration du pack de paramètres de fonction
    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); // erreur : hors limites
static_assert(element_at<0>() == 1); // erreur : hors limites, pack vide
template <std::size_t I, typename Tup>
constexpr auto structured_binding_element_at(Tup tup)
{
    auto [...elems] = tup;
    // 'elems' introduit dans la déclaration du pack de liaisons structurées
    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' introduit dans la déclaration du pack de paramètres template constants
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' introduit dans la capture d'initialisation lambda 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");

L'indexation d'un ensemble d'expressions complexes autre qu'une expression d'identifiant n'est pas autorisée.

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

L'application de decltype à une expression d'indexation de paquet est identique à l'application de decltype à une expression d'identifiant.

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

Spécificateur d'indexation de paquet

typedef-name ...[ expression ]

Le spécificateur d'indexation de paquet dénote le computed-type-specifier , le type de l'élément de paquet P I . Le typedef-name doit être introduit par la déclaration du type template parameter pack .

template <typename... Ts>
using last_type_t = Ts...[sizeof...(Ts) - 1];
static_assert(std::is_same_v<last_type_t<>, int>); // erreur : hors limites
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*>);

Le spécificateur d'indexation de pack peut apparaître comme :

Le spécificateur d'indexation de paquet peut être utilisé dans la liste de paramètres de fonction ou de constructeur pour établir des contextes non déduits dans la déduction d'arguments de template.

template <typename...>
struct type_seq {};
template <typename... Ts>
auto f(Ts...[0] arg, type_seq<Ts...>)
{
    return arg;
}
// OK : "Hello" est implicitement converti en 'std::string_view'
std::same_as<std::string_view> auto a = f("Hello", type_seq<std::string_view>{});
// Erreur : "Ok" n'est pas convertible en 'int'
std::same_as<int> auto b = f("Ok", type_seq<int, const char*>{});

Notes

Avant C++26, Ts... [ N ] était une syntaxe valide pour déclarer un pack de paramètres de fonction de tableaux anonymes de taille N , où les types des paramètres étaient ensuite ajustés en pointeurs. Depuis C++26, Ts... [ 1 ] est interprété comme un spécificateur d'indexation de pack qui changerait le comportement ci-dessous en #2. Pour préserver le premier comportement, le pack de paramètres de fonction doit être nommé, ou manuellement ajusté en un pack de types pointeurs.

template <typename... Ts>
void f(Ts... [1]);
template <typename... Ts>
void g(Ts... args[1]);
template <typename... Ts>
void h(Ts*...); // plus clair mais plus permissif : Ts... peut contenir void cv ou des types de fonction
void foo() 
{
    f<char, bool>(nullptr, nullptr);
    // comportement #1 (avant C++26) :
    //  appelle 'void f<char, bool>(char*, bool*)' (alias 'f<char, bool>(char[1], bool[1])')
    // comportement #2 (depuis C++26) : 
    //  erreur : supposément appelé 'void f<char, bool>(bool)'
    //  mais fourni avec 2 arguments au lieu de 1
    g<char, bool>(nullptr, nullptr);
    // appelle 'g<char, bool>(char*, bool*)' (alias 'g<char, bool>(char[1], bool[1])')
    h<char, bool>(nullptr, nullptr);
    // appelle 'h<char, bool>(char*, bool*)'
}
Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_pack_indexing 202311L (C++26) Indexation de paquets

Exemple

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