Pack indexing (since C++26)
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) | ||||||||
| 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 :
      
- pack de paramètres de modèle constant ,
 - pack de paramètres de fonction ,
 - pack de captures d'initialisation lambda , ou
 - pack de liaisons structurées .
 
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 :
- un spécificateur de type simple ,
 - un spécificateur de classe de base ,
 - un spécificateur de nom imbriqué , ou
 - le type d'un appel explicite au destructeur .
 
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)); }