Espacios de nombres
Variantes
Acciones

Indexación de paquetes (desde C++26)

De cppreference.com
< cpp‎ | language
 
 
Lenguaje C++
Temas generales
Control de flujo
Instrucciones de ejecución condicionales
Instrucciones de iteración (bucles)
Declaraciones de salto
Funciones
Declaración de funciones
Declaración de funciones lambda
Especificador inline
Especificación de excepciones (hasta C++20)
Especificador noexcept (C++11)
Excepciones
Espacios de nombres
Tipos
Especificadores
decltype (C++11)
auto (C++11)
alignas (C++11)
Especificadores de duración de almacenamiento
Inicialización
Expresiones
Representaciones alternas
Literales
Booleanos - Enteros - De punto flotante
De carácter - De cadena - nullptr (C++11)
Definidos por el usuario (C++11)
Utilidades
Atributos (C++11)
Tipos
Declaración de typedef
Declaración de alias de tipo (C++11)
Conversiones
Conversiones implícitas - Conversiones explícitas
static_cast - dynamic_cast
const_cast - reinterpret_cast
Asignación de memoria
Clases
Propiedades de funciones específicas de la clase
Funciones miembro especiales
Plantillas
Misceláneos
 
 
 
 

Accede al elemento de un paquete en un subíndice especificado.

Contenido

[editar] Sintaxis

expresión-id ...[ expression ] (1)
typedef-name ...[ expression ] (2)
1) Expresión de indexación de paquete
2) Especificador de indexación de paquete
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 [0sizeof...(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:

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:

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