This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

4483. Multidimensional arrays are not supported by meta::reflect_constant_array and related functions

Section: 21.4.3 [meta.define.static] Status: New Submitter: Tomasz KamiƄski Opened: 2025-11-27 Last modified: 2025-11-27

Priority: Not Prioritized

View other active issues in [meta.define.static].

View all other issues in [meta.define.static].

View all issues with New status.

Discussion:

As any array type (even of structural types) is not considered an structural type, per 13.2 [temp.param] p12, any invocation of reflect_constant_array/define_static_array with a multidimensional array or span of arrays is ill-formed due to the Mandates in 21.4.3 [meta.define.static] p8 that requires range value type to be structural.

As a consequence, constant_of currently supports only single-dimensional arrays (reflect_constant_array strips outermost extents), while multi-dimensional arrays are rejected.

Furthermore, define_static_object currently uses define_static_array(span(addressof(t), 1)).data(), for array types. Since for T[N] input this creates an multidimensional T[1][N] constant parameter object, this function does not support arrays at all. Creating a distinct template parameter object leads to emission of (otherwise unnecessary) additional symbols, and breaks the invariant that for all supported object types &constant_of(o) == define_static_object(o). We should use reflect_constant_array for arrays directly.

The Throws clause of reflect_constant_array was updated to include any exception thrown by iteration over range.

Proposed resolution:

This wording is relative to N5014 amended with changes from LWG 4432(i).

  1. Modify 21.4.3 [meta.define.static] as indicated:

    template<ranges::input_range R>
      consteval info reflect_constant_array(R&& r);
    

    -8- Let TU be ranges::range_value_t<R> and T be remove_all_extents_<U> ei be static_cast<T>(*iti), where iti is an iterator to the ith element of r.

    -9- Mandates:

    • (9.1) — T is a structural type (13.2 [temp.param]), is_constructible_v<T, ranges::range_reference_t<R>> is true, and
    • (9.2) — T satisfies copy_constructible, and
    • (9.3) — if U is not an array type, then is_constructible_v<T, ranges::range_reference_t<R>> is true.

    -10- Let V be the pack of values of type info of the same size as r, where the ith element is

    • (10.1) — reflect_constant_array(*iti) if U is an array type,
    • (10.2) — reflect_constant(static_cast<T>(*iti)ei) otherwise,
    and iti is an iterator to the ith element of r.

    -11- Let P be

    • (11.1) — If sizeof...(V) > 0 is true, then the template parameter object (13.2 [temp.param]) of type const T[sizeof...(V)] initialized with {[:V:]...}, such that constant_of(P[I]) == V...[I] is true for all I in range [0, sizeof...(V)).
    • (11.2) — Otherwise, the template parameter object of type const array<T, 0> initialized with {}.

    -12- Returns: ^^P.

    -13- Throws: Any exception thrown by increment and dereference operations on iterator to r and comparison of such iterator to sentinel. Any exception thrown by the evaluation of any argument of reflect_constant.ei, or meta::exception if evaluation of any reflect_constant(ei)evaluation of reflect_constant or reflect_constant_array would exit via an exception.

    […]
    template<class T>
      consteval const remove_cvref_t<T>* define_static_object(T&& t);
    

    -15- Effects:Equivalent to:

    using U = remove_cvref_t<T>;
    if constexpr (meta::is_class_type(^^U)) {
      return addressof(meta::extract<const U&>(meta::reflect_constant(std::forward<T>(t))));
    } else if constexpr (meta::is_array_type(^^U)) {
      return addressof(meta::extract<const U&>(meta::reflect_constant_array(std::forward<T>(t))));
    } else {
      return define_static_array(span(addressof(t), 1)).data();
    }