Indexación de paquetes (desde C++26)
Accede al elemento de un paquete en un subíndice especificado.
Contenido |
[editar] Sintaxis
expresión-id ...[ expression ]
|
(1) | ||||||||
typedef-name ...[ expression ]
|
(2) | ||||||||
typedef-name | - | Un identificador o un simple-template-id que denomina un paquete. |
expresión-id | - | Una expresión id que denomina un paquete. |
expression | - | Una expresión constante convertida I de tipo std::size_t designada como un índice donde I se encuentra dentro del rango [ 0, sizeof...(P)) para algún paquete P en la indexación de paquete.
|
[editar] Explicación
La indexación de paquetes es una expansión de paquete del paquete no expandido seguida de puntos suspensivos e índice dentro del subíndice. Hay dos tipos de indexación de paquetes: expresión de indexación de paquete y especificador de indexación de paquete.
Sea P
un paquete no vacío que contiene P0, P1, ..., Pn-1
y I
un índice válido, la instanciación de la expansión P...[I]
produce el elemento de paquete PI
de P
.
No se permite indexar un paquete con un índice de expresión no constante I
.
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 no constante auto d = args...[runtime_idx()]; // ERROR: 'runtime_idx()' no es una expresión no constante }
No es posible indexar un paquete de parámetros de plantilla de plantilla.
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
[editar] Expresión de indexación de paquete
expresión-id ...[ expression ]
|
|||||||||
La expresión de indexación de paquete denota la expresión-id, la expresión del elemento de paquete PI
. La expresión-id se introducirá mediante la declaración de:
- Paquete de parámetros de plantilla de no tipo,
- Paquete de parámetros de función,
- Paquete de captura de inicialización de lambda, o
- Paquete de vínculo 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 de paquete de vínculo 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 de paquete de parámetros de plantilla de no tipo 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 el paquete de captura inicial de lambda return members...[I] + op; }; } static_assert(foo<0>(4, "Hello", true)(5) == 9); static_assert(foo<1>(3, std::string("C++"))("26") == "C++26");
No se permite la indexación de paquetes de expresiones complejas distintas de expresión-id.
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 paquetes es lo mismo que aplicar decltype
a expresión-id.
void f() { [](auto... args) { using T0 = decltype(args...[0]); // 'T0' es 'double' using T1 = decltype((args...[0])); // 'T1' es 'double&' }(3.14); }
[editar] Especificador de indexación de paquete
typedef-name ...[ expression ]
|
|||||||||
El especificador de indexación de paquete denota el especificador de tipo calculado, el tipo de elemento del paquete PI
. El typedef-name se debe introducir mediante la declaración de paquete de parámetros de plantilla de tipo.
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 paquete 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 se puede utilizar 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; } // OK: "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*>{});
[editar] Notas
Antes de C++26, Ts...[N] era una sintaxis válida para declarar paquetes de parámetros de función de arrays sin nombre de tamaño N, donde los tipos de parámetros se ajustaban a punteros. Desde C++26, Ts...[1] se interpreta como un especificador de indexación de paquetes que cambiaría el comportamiento siguiente a #2. Para conservar 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): // invoca void 'f<char, bool>(char*, bool*)' (también conocido como 'f<char, bool>(char[1], bool[1])') // comportamiento #2 (desde C++26): // ERROR: supuestamente se invoca 'void f<char, bool>(bool)' // pero se le proporcionan 2 argumentos en lugar de 1 g<char, bool>(nullptr, nullptr); // llama a 'g<char, bool>(char*, bool*)' (aka 'g<char, bool>(char[1], bool[1])') h<char, bool>(nullptr, nullptr); // llama a 'h<char, bool>(char*, bool*)' }
Macro de prueba de característica | Valor | Estándar | Comentario |
---|---|---|---|
__cpp_pack_indexing |
202311L | (C++26) | Indexación de paquetes |
[editar] 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)); }