Considering the example below, where t
is declared as int[1]
; is the initialization of a
in t a = t{};
legal according to the C++20 standard? Should the behavior of initialization t a = t{};
in any way be affected by marking a
as constexpr
? What does the C++20 standard say about this? And what about the initialization of constexpr auto a = t{};
? Why does MSVC accept it?
static_assert([]{
using t = int[1];
constexpr t a{}; // all ok
constexpr t a = {}; // all ok
constexpr t a = t{}; // clang nope, gcc ok, msvc ok
t a = t{}; // clang nope, gcc ok, msvc nope
constexpr auto a = t{}; // clang nope, gcc nope, msvc ok
return true;
}());
The error message from Clang:
<source>:4:7: error: array initializer must be an initializer list
4 | t a = t{};
| ^
The error message from GCC:
<source>:4:24: error: taking address of temporary array
4 | constexpr auto a = t{};
| ^~~
The error message MSVC:
<source>(4): error C2075: 'a': initialization requires a brace-enclosed
initializer list
In C++17 constexpr t a = t{};
and t a = t{};
are both ill-formed, because initialization of a
falls through until [dcl.init]/17.5 (of draft N4659), which effectively demands that arrays, with an exception for initialization of character arrays by string literals, can only be initialized by an initializer of the form {/*...*/}
or = {/*...*/}
or ()
, where /*...*/
is an optional comma-separated list of initializers. None of these match t{}
.
In C++20 aggregate initialization from parentheses was added, so that initializing arrays with an initializer of the form (/*...*/)
became allowed. So the fall-through case 17.5 in the initialization rules was replaced with rules for parenthesized aggregate initialization (see [dcl.init.general]/16.5 in draft N4868). Unfortunately the new wording assumes that if the bullet is reached, the array initializer must have the form (/*...*/)
, ignoring the case in your question where t{}
doesn't match any form.
So the C++20 standard lacks a specification for these cases.
CWG issue 2824 has already been reported, although it is concerned with the special case where the initializer is a string literal, not a cast expression, and the array not a character array. Regardless, the resolution which is marked as "tentatively ready" and approved by CWG would make all of the remaining initializer forms ill-formed again, as in C++17.
So, once this is a proper defect report and implemented by the compiler vendors, both constexpr t a = t{};
and t a = t{};
should be ill-formed.
constexpr auto a = t{};
is ill-formed, because the initialization is not a constant expression. You are trying to store a pointer into the temporary array materialized from t{}
. This is not a permitted result of a constant expression per [expr.const]/11.2.
Without constexpr
it should be fine, but the pointer is immediately dangling after the initialization, because the temporary array is destroyed again. However, the initialization itself should be permitted, because [conv.array] specifies that the array-to-pointer conversion also applies to array rvalues.