template for расширение (начиная с C++26)
Раскрывает конструкцию языка, определяемую во время компиляции, в её элементы.
Используется как более читаемый эквивалент нескольких инструкций, применяющих одну и ту же логику к нескольким (возможно, разнородным) выражениям.
Синтаксис
attr (необязательно) template for ( init-statement (необязательно) item-declaration : expansion-initializer ) compound-statement |
| attr | - | любое количество атрибутов |
| init-statement | - | одно из следующего:
Обратите внимание, что любой init-statement должен заканчиваться точкой с запятой. Поэтому его часто неформально описывают как выражение или объявление, за которым следует точка с запятой. |
| item-declaration | - | одно из следующего:
|
| expansion-initializer | - | одно из следующего:
|
| compound-statement | - | любая составная инструкция |
Пояснение
Если цикл нужно завершить внутри compound-statement , в качестве завершающей инструкции можно использовать инструкцию break.
Если текущую итерацию нужно завершить внутри compound-statement , в качестве сокращённого способа можно использовать инструкцию continue.
В зависимости от синтаксиса и свойств expansion-initializer, инструкция templatefor может относиться к одной из следующих трёх категорий.
Перечисляющая инструкция расширения
Если expansion-initializer представляет собой список выражений в фигурных скобках, инструкция является перечисляющей инструкцией расширения .
Пусть N — количество элементов в списке выражений, а /*expr-I*/ — I-й элемент списка выражений (индексация начинается с 0). Тогда перечисляющая инструкция расширения эквивалентна следующему, за исключением времени жизни временных объектов, создаваемых в expansion-initializer (см. ниже):
{ init-statement { item-declaration = /*expr-0*/; compound-statement } // ... { item-declaration = /*expr-(N-1)*/; compound-statement } }
Список выражений может быть результатом раскрытия пакета:
Итерирующая инструкция расширения
Если expansion-initializer — выражение, допускающее итерацию расширения (см. ниже), инструкция является итерирующей инструкцией расширения .
Итерирующая инструкция расширения эквивалентна следующему, за исключением времени жизни временных объектов, создаваемых в expansion-initializer (см. ниже); переменные и выражения, заключённые в /* */, приведены только для пояснения:
constexpr auto /*N*/ = [&] consteval { std::ptrdiff_t result = 0; auto b = /*begin-expr*/; auto e = /*end-expr*/; for (; b != e; ++b) ++result; return result; }(); { init-statement constexpr(optional) decltype(auto) /*range*/ = (expansion-initializer ); constexpr(optional) auto /*begin*/ = /*begin-expr*/; { constexpr(optional) /*iter*/ = /*begin*/ + decltype(begin - begin){0}; item-declaration = */*iter*/; compound-statement } // ... { constexpr(optional) /*iter*/ = /*begin*/ + decltype(begin - begin){/*N*/ - 1}; item-declaration = */*iter*/; compound-statement } }
/*range*/, /*begin*/ и /*iter*/ объявляются constexpr тогда и только тогда, когда item-declaration содержит спецификатор constexpr.
Выражения /*begin-expr*/ и /*end-expr*/, используемые только для пояснения, определяются следующим образом:
- Если тип /*range*/ — ссылка на тип массива
R: - Если
Rимеет размерN, то /*begin-expr*/ —/*range*/, а /*end-expr*/ —/*range*/ + N. - Если
R— массив неизвестного размера или массив неполного типа, программа некорректна. - Если тип /*range*/ — ссылка на тип класса
Cи поиск в области видимостиCимён «begin» и «end» обнаруживает хотя бы одно объявление каждого из них, то /*begin-expr*/ —/*range*/.begin(), а /*end-expr*/ —/*range*/.end(). - В противном случае /*begin-expr*/ —
begin(/*range*/), а /*end-expr*/ —end(/*range*/), где имена «begin» и «end» находятся с помощью поиска, зависящего от аргументов (поиск без ADL не выполняется).
Если expansion-initializer — выражение не-массивного типа, а и /*begin-expr*/, и /*end-expr*/ корректны согласно приведённым выше правилам, то expansion-initializer допускает итерацию расширения .
Инструкция расширения с деструктуризацией
Если expansion-initializer — выражение, не допускающее итерацию расширения, инструкция является итерирующей инструкцией расширения .
Пусть N — размер структурированной привязки типа expansion-initializer . Тогда итерирующая инструкция расширения эквивалентна следующему, за исключением времени жизни временных объектов, создаваемых в expansion-initializer (см. ниже); переменные и выражения, заключённые в /* */, приведены только для пояснения:
- Если
Nравно нулю, инструкция эквивалентна следующему:
{ init-statement constexpr(optional) auto&& /*range*/ = (expansion-initializer ); }
- В противном случае инструкция эквивалентна следующему:
{ init-statement constexpr(optional) auto&& [/* u0, u1, ..., u(N-1) */] = (expansion-initializer ); { item-declaration = /*v0*/; compound-statement } // ... { item-declaration = /*v(N-1)*/; compound-statement } }
/*range*/, /*begin*/ и /*iter*/ объявляются constexpr тогда и только тогда, когда item-declaration содержит спецификатор constexpr.
Если expansion-initializer — lvalue, то /*vI*/ — /*uI*/; в противном случае /*vI*/ — static_cast<decltype(/*uI*/)&&>(/*uI*/).
Временные объекты в инициализаторе расширения
Для перечисляющих инструкций расширения, если временный объект создаётся в элементе expr списка выражений expansion-initializer и должен быть уничтожен в конце полного выражения expr, время жизни объекта продлевается до конца времени жизни item-declaration, инициализированного из expr:
// if foo() returns by value
template for (auto& x : {foo().items()}) { /* ... */ }
// expands to:
{
// for the temporary object returned from foo():
{
auto& x = foo().items(); // the object would be destroyed at the semicolon,
// but its lifetime is extended to the end of the block
// (same as the lifetime of “x”)
{ /* ... */ } // using “x” here is well-defined
}
}
Для итерирующих инструкций расширения и инструкций расширения с деструктуризацией, если временный объект создаётся в expansion-initializer и должен быть уничтожен в конце этого expansion-initializer , время жизни объекта продлевается до конца времени жизни ссылки, инициализированной из expansion-initializer :
struct T
{
std::vector<int> vec{1, 2};
std::tuple<int> tup{3, 4};
};
template for (auto& x: T().vec) { /* ... */ }
// expands to:
{
// for the temporary object returned from T():
decltype(auto) range = (T().vec); // the object would be destroyed at the semicolon,
// but its lifetime is extended to the end of the
// block (same as the lifetime of “range”)
auto begin = range.begin();
/* expanded compound statements */ // accessing the vector elements here
// is well-defined
}
template for (auto& x: T().tup) { /* ... */ }
// expands to:
{
// for the temporary object returned from T():
auto&& [u0, u1] = T().tup; // the object would be destroyed at the semicolon,
// but its lifetime is extended to the end of the block
// (same as the lifetimes of “u0” and “u1”)
/* expanded compound statements */ // accessing the “u0” and “u1” here
// is well-defined
}
Примечания
| Макрос проверки поддержки функции | Значение | Стандарт | Функция |
|---|---|---|---|
__cpp_expansion_statements | 202506L | (C++26) | templatefor |
Ключевые слова
Пример
#include <array>
#include <vector>
constexpr int f1(const auto&... containers)
{
int result = 0;
template for (const auto& c : {containers...}) // enumerating expansion statement
result += c[0];
return result;
}
constexpr int f2()
{
constexpr std::array<int, 3> arr{1, 2, 3};
int result = 0;
template for (constexpr int s : auto(arr)) // iterating expansion statement
result += s;
return result;
}
struct S
{
int i;
short s;
};
constexpr long f3(S s)
{
long result = 0;
template for (auto x : s) // destructuring expansion statement
result += sizeof(x);
return result;
}
int main()
{
constexpr int c1[] = {1, 2, 3};
constexpr int c2[] = {4, 3, 2, 1};
static_assert(f1(c1, c2) == 5); // c1[0] + c2[0]
static_assert(f2() == 6); // 1 + 2 + 3
static_assert(f3(S{}) == sizeof(int) + sizeof(short));
}
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/template_for