This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115e. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2024-11-11
Consider an example like the following:
struct A { constexpr A(int i) : val(i) { } constexpr operator int() const { return val; } constexpr operator float() const { return val; } private: int val; }; constexpr A a = 42; int ary[a];
According to 9.3.4.5 [dcl.array] paragraph 1, the array bound expression
shall be a converted constant expression of type std::size_t (7.7 [expr.const]).
The user-defined conversion to float would involve a floating-integral conversion (7.3.11 [conv.fpint]; however, such a conversion is not permitted by the list of acceptable conversions in 7.7 [expr.const] paragraph 10:
A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only
user-defined conversions,
lvalue-to-rvalue conversions (7.3.2 [conv.lval]),
array-to-pointer conversions (7.3.3 [conv.array]),
function-to-pointer conversions (7.3.4 [conv.func]),
qualification conversions (7.3.6 [conv.qual]),
integral promotions (7.3.7 [conv.prom]),
integral conversions (7.3.9 [conv.integral]) other than narrowing conversions (9.4.5 [dcl.init.list]),
null pointer conversions (7.3.12 [conv.ptr]) from std::nullptr_t,
null member pointer conversions (7.3.13 [conv.mem]) from std::nullptr_t, and
function pointer conversions (7.3.14 [conv.fctptr]),
and where the reference binding (if any) binds directly.
It is not clear whether this list is intended to restrict the set of viable user-defined conversions, and there is implementation divergence on this point: clang accepts the example above, while g++ rejects it, presumably on the basis of an ambiguous conversion.
Notes from the August, 2020 teleconference:
No direction was established pending information about why the example is accepted by clang.
Additional note, December, 2020:
The clang behavior turns out to have been an oversight, corrected in the current version, so the example is now rejected by both compilers. However, it is unclear that this is desirable. In particular, given the example above, a can be used without error as a bit-field width, as an enumerator value, and as the operand of alignas. Presumably the difference between these integral constant expression contexts and an array bound is the fact that the target type is known to be size_t. However, both bit-field widths and alignas operands are also required to be non-negative. Furthermore, the definition of an “erroneous” array bound in 7.6.2.8 [expr.new] paragraph 9 goes to awkward lengths to check for negative values as the result of user-defined conversions, which might argue in favor of reconsidering the converted constant expression treatment of array bounds.
Notes from the February, 2021 teleconference:
CWG agreed with the considerations in the December, 2020 note, feeling that the difference in treatment between integral constant expressions and a converted constant expression to a specific integral type is somewhat gratuitous. However, it was felt that code like that of the example was unlikely to occur often in real-world code.