Spec-Zone.ru › C++

template for расширение (начиная с C++26)

Раскрывает конструкцию языка, определяемую во время компиляции, в её элементы.

Используется как более читаемый эквивалент нескольких инструкций, применяющих одну и ту же логику к нескольким (возможно, разнородным) выражениям.

Синтаксис

attr (необязательно) template for ( init-statement (необязательно) item-declaration : expansion-initializer ) compound-statement
attr - любое количество атрибутов
init-statement - одно из следующего:
  • инструкция-выражение (которая может быть пустой инструкцией ;)
  • простое объявление (обычно объявление переменной с инициализатором); в нём может объявляться произвольное количество переменных или может быть объявление структурированной привязки
  • объявление псевдонима

Обратите внимание, что любой init-statement должен заканчиваться точкой с запятой. Поэтому его часто неформально описывают как выражение или объявление, за которым следует точка с запятой.

item-declaration - одно из следующего:
  • простое объявление со следующими ограничениями:
    • В нём есть только один декларатор.
    • У декларатора не должно быть инициализатора.
    • Последовательность спецификаторов объявления может содержать только спецификаторы типа и constexpr и не может определять класс или перечисление.
  • объявление структурированной привязки
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

Ключевые слова

for, template

Пример

#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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API