Pack indexing (since C++26)
Обращается к элементу пака по указанному индексу.
Содержание |
Синтаксис
id-expression
...[
expression
]
|
(1) | ||||||||
typedef-name
...[
expression
]
|
(2) | ||||||||
` также сохранены без изменений.
| typedef-name | - | идентификатор или simple-template-id , который обозначает пакет |
| id-expression | - | id-expression , который обозначает пакет |
| expression | - |
converted constant expression
I
типа
std::
size_t
, обозначающая индекс, где
I
находится в диапазоне
[
0
,
sizeof...
(
P
)
)
для некоторого пакета
P
в индексации пакетов
|
Объяснение
Индексирование пакетов представляет собой развертывание пакета неразвернутого пакета, за которым следует многоточие и индекс внутри подстрочного индекса. Существует два вида индексирования пакетов: выражение индексирования пакета и спецификатор индексирования пакета.
Пусть
P
будет непустым пакетом, содержащим
P
0
, P
1
, ..., P
n-1
и
I
будет допустимым индексом, тогда инстанцирование раскрытия
P...[I]
даёт элемент пакета
P
I
из
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]; // ошибка: 'm' не является константным выражением auto d = args...[runtime_idx()]; // ошибка: 'runtime_idx()' не является константным выражением }
Индексирование пакета параметров шаблонов шаблонов невозможно.
template <template <typename...> typename... Temps> using A = Temps...[0]<>; // ошибка: 'Temps' является пакетом параметров шаблонов-шаблонов template <template <typename...> typename... Temps> using B = Temps<>...[0]; // ошибка: 'Temps<>' не обозначает имя пакета // хотя это simple-template-id
Выражение индексации пакета
id-expression
...[
expression
]
|
|||||||||
Выражение индексирования пакета обозначает
id-expression
, выражение элемента пакета
P
I
.
id-expression
должно быть введено объявлением:
- пакет постоянных параметров шаблона ,
- пакет параметров функции ,
- пакет инициализирующего захвата лямбда-выражения , или
- пакет структурированной привязки .
template <std::size_t I, typename... Ts> constexpr auto element_at(Ts... args) { // 'args' объявлен в объявлении пакета параметров функции 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); // ошибка: выход за границы static_assert(element_at<0>() == 1); // ошибка: выход за границы, пустой пакет template <std::size_t I, typename Tup> constexpr auto structured_binding_element_at(Tup tup) { auto [...elems] = tup; // 'elems' объявлен в объявлении пакета структурированных привязок 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' объявлен в объявлении пакета параметров шаблона констант 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' объявлен в пакете захвата инициализации лямбды return members...[I] + op; }; } static_assert(foo<0>(4, "Hello", true)(5) == 9); static_assert(foo<1>(3, std::string("C++"))("26") == "C++26");
Индексирование пакета сложных выражений, отличных от id-expression, не допускается.
template <std::size_t I, auto... Vals> constexpr auto identity_at = (Vals)...[I]; // ошибка // используйте 'Vals...[I]' вместо этого template <std::size_t I, std::size_t... Vals> constexpr std::size_t triple_at = (Vals * 3)...[I]; // ошибка // используйте 'Vals...[I] * 3' вместо этого template <std::size_t I, typename... Args> constexpr decltype(auto) get(Args&&... args) noexcept { return std::forward<Args>(args)...[I]; // ошибка // используйте 'std::forward<Args...[I]>(args...[I])' вместо этого }
Применение
decltype
к выражению индексации пакета аналогично применению
decltype
к id-выражению.
void f() { [](auto... args) { using T0 = decltype(args...[0]); // 'T0' имеет тип 'double' using T1 = decltype((args...[0])); // 'T1' имеет тип 'double&' }(3.14); }
Спецификатор индексации пакета
typedef-name
...[
expression
]
|
|||||||||
Спецификатор индексирования пакета обозначает
computed-type-specifier
, тип элемента пакета
P
I
.
typedef-name
должен быть введён объявлением
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>); // ошибка: выход за границы 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*>);
Спецификатор индексации пакета может встречаться в виде:
- простой спецификатор типа simple type specifier ,
- спецификатор базового класса base class specifier ,
- спецификатор вложенного имени nested name specifier , или
- тип явного вызова деструктора type of an explicit destructor call .
Спецификатор индексирования пакета может использоваться в списке параметров функции или конструктора для создания невыводимых контекстов при выводе шаблонных аргументов.
template <typename...> struct type_seq {}; template <typename... Ts> auto f(Ts...[0] arg, type_seq<Ts...>) { return arg; } // OK: "Hello" неявно преобразуется в 'std::string_view' std::same_as<std::string_view> auto a = f("Hello", type_seq<std::string_view>{}); // Ошибка: "Ok" не конвертируется в '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*...); // более ясная, но более разрешительная форма: Ts... может содержать cv void или типы функций void foo() { f<char, bool>(nullptr, nullptr); // поведение #1 (до C++26): // вызов void 'f<char, bool>(char*, bool*)' (также 'f<char, bool>(char[1], bool[1])') // поведение #2 (начиная с C++26): // ошибка: предположительный вызов 'void f<char, bool>(bool)' // но предоставлено 2 аргумента вместо 1 g<char, bool>(nullptr, nullptr); // вызов 'g<char, bool>(char*, bool*)' (также 'g<char, bool>(char[1], bool[1])') h<char, bool>(nullptr, nullptr); // вызов '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)); }