CWG Issue 3044 (original) (raw)

This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 118f. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2025-11-07


3044. Iterating expansion statements woes

Section: 8.7 [stmt.expand]Status: readySubmitter: Jakub JelinekDate: 2025-07-08

(From submissions#725 and#730.)

The rewrite of iterating expansion statements contains:

static constexpr auto iter = begin + i;

where the type of i is unspecified, but overload resolution of + depends on the type of i.

Furthermore, the rewrite contains

for (auto i = begin ; i != end ; ++i, ++result);

which might invoke an overloaded comma operator, which is undesirable.

Furthermore, because of the use of static in the rewrite, the example in 8.7 [stmt.expand] paragraph 7 is ill-formed.

Possible resolution (July 2025) [SUPERSEDED]:

  1. Change in 8.7 [stmt.expand] bullet 5.2 as follows:

    Otherwise, if S is an iterating expansion statement, S is equivalent to:

    {
    init-statement
    static constexpr auto&& range = expansion-initializer ;
    static constexpr auto begin = begin-expr; // see 8.6.5 [stmt.ranged]
    static constexpr auto end = end-expr; // see 8.6.5 [stmt.ranged]
    S0
    .
    .
    .
    SN-1
    }

    where N is the result of evaluating the expression

    [] consteval {
    std::ptrdiff_t result = 0;
    for (auto i = begin ; i != end ; ++i, ++result) ++result;
    return result; // distance from begin to end
    }()

    and Si is

    {
    static constexpr auto iter = begin + i 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 decltype(begin - begin); 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 (7.7 [expr.const]). -- end note]

  2. No change in 8.7 [stmt.expand] paragraph 7:

    [ Example 2:

    consteval int f() {
    constexpr std::array<int, 3> arr {1, 2, 3};
    int result = 0;
    template for (constexpr int s : arr) { // OK, iterating expansion statement
    result += sizeof(char[s]);

}
return result;
}
static_assert(f() == 6);

—_end example_]

Proposed resolution (approved by CWG 2025-11-06):

Change in 8.7 [stmt.expand] bullet 5.2 as follows:

Otherwise, if S is an iterating expansion statement, S is equivalent to:

{ init-statement static constexpr auto&& range = expansion-initializer ; static constexpr auto begin = begin-expr; // see 8.6.5 [stmt.ranged] static constexpr auto end = end-expr; // see 8.6.5 [stmt.ranged] S0 . . . SN-1 }

where N is the result of evaluating the expression

[] consteval { std::ptrdiff_t result = 0; for (auto i = begin ; i != end ; ++i, ++result) ++result; return result; // distance from begin to end }()

and Si is

{ static constexpr auto iter = begin + i 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 (7.7 [expr.const]). -- end note]