Pack indexing (since C++26)
Accede al elemento de un pack en un índice especificado.
Contenidos |
Sintaxis
expresión-de-identificador
...[
expresión
]
|
(1) | ||||||||
nombre-de-tipo-definido
...[
expresión
]
|
(2) | ||||||||
| typedef-name | - | un identificador o un simple-template-id que nombra un pack |
| id-expression | - | una id-expression que nombra un pack |
| expression | - |
una
expresión constante convertida
I
de tipo
std::
size_t
designada como índice donde
I
está dentro del rango
[
0
,
sizeof...
(
P
)
)
para algún pack
P
en indexación de packs
|
Explicación
La indexación de paquetes es una expansión de paquetes del paquete no expandido seguida de puntos suspensivos y un índice dentro del subíndice. Existen dos tipos de indexación de paquetes: expresión de indexación de paquetes y especificador de indexación de paquetes.
Sea
P
un paquete no vacío que contiene
P
0
, P
1
, ..., P
n-1
e
I
un índice válido, la instanciación de la expansión
P...[I]
produce el elemento del paquete
P
I
de
P
.
Indexar un paquete con índice de expresión no constante
I
no está permitido.
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' no es una expresión constante auto d = args...[runtime_idx()]; // error: 'runtime_idx()' no es una expresión constante }
Indexar un paquete de parámetros de plantilla de plantilla no es posible.
template <template <typename...> typename... Temps> using A = Temps...[0]<>; // error: 'Temps' es un paquete de parámetros de plantilla de plantilla template <template <typename...> typename... Temps> using B = Temps<>...[0]; // error: 'Temps<>' no denota un nombre de paquete // aunque es un simple-template-id
Expresión de indexación de paquete
expresión-de-identificador
...[
expresión
]
|
|||||||||
La expresión de indexación de paquete denota la
id-expression
, la expresión del elemento del paquete
P
I
. La
id-expression
será introducida por la declaración de:
- paquete de parámetros de plantilla constante ,
- paquete de parámetros de función ,
- paquete de captura inicial de lambda , o
- paquete de enlace estructurado .
template <std::size_t I, typename... Ts> constexpr auto element_at(Ts... args) { // 'args' introducido en la declaración del paquete de parámetros de función 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: fuera de límites static_assert(element_at<0>() == 1); // error: fuera de límites, paquete vacío template <std::size_t I, typename Tup> constexpr auto structured_binding_element_at(Tup tup) { auto [...elems] = tup; // 'elems' introducido en la declaración del paquete de enlace estructurado 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' introducido en la declaración del paquete de parámetros de plantilla constante 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' introducido en la captura de inicialización de 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");
La indexación de paquetes de expresiones complejas distintas de expresiones de identificación no está permitida.
template <std::size_t I, auto... Vals> constexpr auto identity_at = (Vals)...[I]; // error // usar 'Vals...[I]' en su lugar template <std::size_t I, std::size_t... Vals> constexpr std::size_t triple_at = (Vals * 3)...[I]; // error // usar 'Vals...[I] * 3' en su lugar template <std::size_t I, typename... Args> constexpr decltype(auto) get(Args&&... args) noexcept { return std::forward<Args>(args)...[I]; // error // usar 'std::forward<Args...[I]>(args...[I])' en su lugar }
Aplicar
decltype
a una expresión de indexación de paquete es lo mismo que aplicar
decltype
a una expresión de identificador.
void f() { [](auto... args) { using T0 = decltype(args...[0]); // 'T0' es 'double' using T1 = decltype((args...[0])); // 'T1' es 'double&' }(3.14); }
Especificador de indexación de paquetes
typedef-name
...[
expression
]
|
|||||||||
El especificador de indexación de paquetes denota el
computed-type-specifier
, el tipo del elemento del paquete
P
I
. El
typedef-name
será introducido por la declaración del
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>); // error: fuera de límites 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*>);
El especificador de indexación de paquetes puede aparecer como:
- un especificador de tipo simple ,
- un especificador de clase base ,
- un especificador de nombre anidado , o
- el tipo de una llamada explícita al destructor .
El especificador de indexación de paquete puede utilizarse en la lista de parámetros de función o constructor para establecer contextos no deducidos en la deducción de argumentos de plantilla.
template <typename...> struct type_seq {}; template <typename... Ts> auto f(Ts...[0] arg, type_seq<Ts...>) { return arg; } // CORRECTO: "Hello" se convierte implícitamente a 'std::string_view' std::same_as<std::string_view> auto a = f("Hello", type_seq<std::string_view>{}); // ERROR: "Ok" no es convertible a 'int' std::same_as<int> auto b = f("Ok", type_seq<int, const char*>{});
Notas
Antes de C++26, Ts... [ N ] era una sintaxis válida para declarar un paquete de parámetros de función de arrays sin nombre de tamaño N , donde los tipos de parámetros se ajustaban posteriormente a punteros. Desde C++26, Ts... [ 1 ] se interpreta como un especificador de indexación de paquetes, lo que cambiaría el comportamiento siguiente al #2. Para preservar el primer comportamiento, el paquete de parámetros de función debe tener nombre o ajustarse manualmente a un paquete de tipos puntero.
template <typename... Ts> void f(Ts... [1]); template <typename... Ts> void g(Ts... args[1]); template <typename... Ts> void h(Ts*...); // más claro pero más permisivo: Ts... puede contener cv void o tipos de función void foo() { f<char, bool>(nullptr, nullptr); // comportamiento #1 (antes de C++26): // llama a 'void f<char, bool>(char*, bool*)' (también 'f<char, bool>(char[1], bool[1])') // comportamiento #2 (desde C++26): // error: supuestamente se llamó a 'void f<char, bool>(bool)' // pero se proporcionaron 2 argumentos en lugar de 1 g<char, bool>(nullptr, nullptr); // llama a 'g<char, bool>(char*, bool*)' (también 'g<char, bool>(char[1], bool[1])') h<char, bool>(nullptr, nullptr); // llama a 'h<char, bool>(char*, bool*)' }
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_pack_indexing
|
202311L
|
(C++26) | Indexación de paquetes |
Ejemplo
#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)); }