[stmt.expand]
8 Statements [stmt]
8.7 Expansion statements [stmt.expand]
For an expression E, let the expressions begin-expr and end-expr be determined as specified in [stmt.ranged].
An expression is expansion-iterable if it does not have array type and either
- begin-expr and end-expr are of the form E.begin() and E.end(), or
- argument-dependent lookups for begin(E) and for end(E) each find at least one function or function template.
An expansion statement is
- an enumerating expansion statement if its expansion-initializer is of the form expansion-init-list;
- otherwise, an iterating expansion statement if its expansion-initializer is an expansion-iterable expression;
- otherwise, a destructuring expansion statement.
An expansion statement S is equivalent to a compound-statement containing instantiations of the for-range-declaration (including its implied initialization), together with the compound-statement of S, as follows:
- If S is an enumerating expansion statement, S is equivalent to: { init-statement ⋮ } where N is the number of elements in the expression-list, is { for-range-declaration = ; compound-statement } and is the element of the expression-list.
- Otherwise, if S is an iterating expansion statement, S is equivalent to:
{
init-statement
constexpr auto&& range = expansion-initializer;
constexpr auto begin = begin-expr;
constexpr auto end = end-expr;
⋮
}
where N is the result of evaluating the expression
[] consteval {
std::ptrdiff_t result = 0;
for (auto i = begin; i != end; ++i) ++result;
return result;
}()
and is
{
constexpr auto iter = begin + decltype(begin - begin){i};
for-range-declaration = *iter;
compound-statement
}
The variables range, begin, end, and iter are defined for exposition only.
The identifier i is considered to be a prvalue of type std::ptrdiff_t; the program is ill-formed if i is not representable as such a value.
[Note 1:
The instantiation is ill-formed if range is not a constant expression ([expr.const]).
— end note]
- Otherwise, S is a destructuring expansion statement and, if N is 0, S is equivalent to: { init-statement constexpr auto&& range = expansion-initializer; } otherwise, S is equivalent to: { init-statement constexpr auto&& [, , …, ] = expansion-initializer; ⋮ } where N is the structured binding size of the type of the expansion-initializer and is { for-range-declaration = ; compound-statement }
[Example 1: consteval int f(auto const&... Containers) { int result = 0; template for (auto const& c : {Containers...}) { result += c[0]; } return result; } constexpr int c1[] = {1, 2, 3}; constexpr int c2[] = {4, 3, 2, 1}; static_assert(f(c1, c2) == 5); — end example]
[Example 2: consteval int f() { constexpr std::array<int, 3> arr {1, 2, 3}; int result = 0; template for (constexpr int s : arr) { result += sizeof(char[s]); } return result; } static_assert(f() == 6); — end example]
[Example 3: struct S { int i; short s; }; consteval long f(S s) { long result = 0; template for (auto x : s) { result += sizeof(x); } return result; } static_assert(f(S{}) == sizeof(int) + sizeof(short)); — end example]