C++20 adopted p0960 - allowing initialization of aggregates from a parenthesized list of values.
The exact wording ([dcl.init] 17.6.2.2) says:
[...] if no constructor is viable, the destination type is an aggregate class, and the initializer is a parenthesized expression-list, the object is initialized as follows.
Let e1 , …, en be the elements of the aggregate ([dcl.init.aggr]).
Let x1, …, xk be the elements of the expression-list.
If k is greater than n, the program is ill-formed.
The element ei is copy-initialized with xi for 1 ≤ i ≤ k . The remaining elements are initialized with their default member initializers [...]
This doesn't allow initialization of inner array with parenthesized list of values:
struct Foo {
int i, j;
};
struct Moo {
int arr[2];
};
int main() {
// before C++20:
Foo foo1{1, 2};
// with C++20:
Foo foo2(1, 2); // p0960! we are good
// before C++20:
Moo moo1{1, 2};
// C++20 - oops p0960 doesn't help here:
Moo moo2(1, 2); // error: too many initializers
// before C++20:
std::array<int, 2> arr1{1, 2}; // OK
std::array<int, 2> arr2({1, 2}); // OK
std::array<int, 2> arr3{{1, 2}}; // OK
// C++20 - oops p0960 doesn't help here:
std::array<int, 2> arr4(1, 2); // error: too many initializers
}
The fact that std::array
cannot be initialized with rounded brackets prevents it from participating in a generic code that creates an object of unknown type T
from a list of values (e.g. an algorithm that uses make_shared
, make_unique
, make_from_tuple
etc.).
Why p0960 didn't take a more simple approach making ()-initialization more like {}?
For example, something like:
if no constructor is viable, the destination type is an aggregate class, and the initializer is a parenthesized expression-list, the object would be initialized as if the values were sent with brace-initialization.