Индексация пакета (начиная с C++26)
Обращается к элементу пакета по заданному индексу.
Синтаксис
id-expression ...[ expression ] | (1) | |
typedef-name ...[ expression ] | (2) |
| typedef-name | - | идентификатор или простой идентификатор шаблона, обозначающий пакет |
| id-expression | - | выражение-идентификатор, обозначающее пакет |
| expression | - | преобразуемое константное выражение I типа std::size_t, задающее индекс, где I находится в диапазоне [0, sizeof...(P)) для некоторого пакета P при индексации пакета |
Пояснение
Индексация пакета — это раскрытие пакета нераскрытого пакета, за которым следуют многоточие и индекс в квадратных скобках. Существует два вида индексации пакета: выражение индексации пакета и спецификатор индексации пакета.
Пусть P — непустой пакет, содержащий P0, P1, ..., Pn-1, а I — допустимый индекс. При инстанцировании раскрытия P...[I] получается элемент пакета PI из P.
Индексация пакета с помощью индекса 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' is not a constant expression
auto d = args...[runtime_idx()]; // error: 'runtime_idx()' is not a constant expression
}
Индексация пакета параметров шаблона-шаблона невозможна.
template <template <typename...> typename... Temps>
using A = Temps...[0]<>; // error: 'Temps' is a pack of template template parameters
template <template <typename...> typename... Temps>
using B = Temps<>...[0]; // error: 'Temps<>' doesn't denote pack name
// although it is a simple-template-id
Выражение индексации пакета
id-expression ...[ expression ] |
Выражение индексации пакета обозначает выражение-идентификатор, выражение элемента пакета PI. id-expression должно быть введено объявлением:
- пакета нетиповых параметров шаблона,
- пакета параметров функции,
- пакета захвата с инициализатором лямбда-выражения или
- пакета структурированной привязки.
template <std::size_t I, typename... Ts>
constexpr auto element_at(Ts... args)
{
// 'args' introduced in function parameter pack declaration
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: out of bounds
static_assert(element_at<0>() == 1); // error: out of bounds, empty pack
template <std::size_t I, typename Tup>
constexpr auto structured_binding_element_at(Tup tup)
{
auto [...elems] = tup;
// 'elems' introduced in structured binding pack declaration
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' introduced in non-type template parameter pack declaration
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' introduced in lambda init-capture 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");
Индексация пакета, состоящего из сложных выражений, отличных от выражений-идентификаторов, не допускается.
template <std::size_t I, auto... Vals>
constexpr auto identity_at = (Vals)...[I]; // error
// use 'Vals...[I]' instead
template <std::size_t I, std::size_t... Vals>
constexpr std::size_t triple_at = (Vals * 3)...[I]; // error
// use 'Vals...[I] * 3' instead
template <std::size_t I, typename... Args>
constexpr decltype(auto) get(Args&&... args) noexcept
{
return std::forward<Args>(args)...[I]; // error
// use 'std::forward<Args...[I]>(args...[I])' instead
}
Применение decltype к выражению индексации пакета эквивалентно применению decltype к выражению-идентификатору.
void f()
{
[](auto... args)
{
using T0 = decltype(args...[0]); // 'T0' is 'double'
using T1 = decltype((args...[0])); // 'T1' is 'double&'
}(3.14);
}
Спецификатор индексации пакета
typedef-name ...[ expression ] |
Спецификатор индексации пакета обозначает спецификатор вычисляемого типа — тип элемента пакета PI. typedef-name должно быть введено объявлением пакета параметров типа шаблона.
template <typename... Ts> using last_type_t = Ts...[sizeof...(Ts) - 1]; static_assert(std::is_same_v<last_type_t<>, int>); // error: out of bounds 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*>);
Спецификатор индексации пакета может выступать в качестве:
- простого спецификатора типа,
- спецификатора базового класса,
- спецификатора вложенного имени или
- типа явного вызова деструктора.
Спецификатор индексации пакета можно использовать в списке параметров функции или конструктора для создания недедуцируемых контекстов при выводе аргументов шаблона.
template <typename...>
struct type_seq {};
template <typename... Ts>
auto f(Ts...[0] arg, type_seq<Ts...>)
{
return arg;
}
// OK: "Hello" is implicitly converted to 'std::string_view'
std::same_as<std::string_view> auto a = f("Hello", type_seq<std::string_view>{});
// Error: "Ok" is not convertible to 'int'
std::same_as<int> auto b = f("Ok", type_seq<int, const char*>{});
Примечания
До C++26 Ts...[N] было допустимым синтаксисом объявления пакета параметров функции из безымянных массивов размера N, типы параметров которых затем преобразовывались в указатели. Начиная с C++26 Ts...[1] интерпретируется как спецификатор индексации пакета, что изменило бы приведенное ниже поведение на вариант № 2. Чтобы сохранить первое поведение, пакет параметров функции необходимо именовать либо вручную преобразовать в пакет типов указателей.
template <typename... Ts>
void f(Ts... [1]);
template <typename... Ts>
void g(Ts... args[1]);
template <typename... Ts>
void h(Ts*...); // clearer but more permissive: Ts... can contain cv void or function types
void foo()
{
f<char, bool>(nullptr, nullptr);
// behavior #1 (before C++26):
// calls void 'f<char, bool>(char*, bool*)' (aka 'f<char, bool>(char[1], bool[1])')
// behavior #2 (since C++26):
// error: supposedly called 'void f<char, bool>(bool)'
// but provided with 2 arguments instead of 1
g<char, bool>(nullptr, nullptr);
// calls 'g<char, bool>(char*, bool*)' (aka 'g<char, bool>(char[1], bool[1])')
h<char, bool>(nullptr, nullptr);
// calls 'h<char, bool>(char*, bool*)'
}
| Макрос проверки поддержки функции | Значение | Стандарт | Функция |
|---|---|---|---|
__cpp_pack_indexing | 202311L | (C++26) | Индексация пакета |
Пример
#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));
}
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/pack_indexing