Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Brace elision in std::array<std::vector>

I'm compiling using g++ for C++ 17. I have the following:

std::array<std::vector<int>, 2> v = {{ {1,2}, {3,4} }};

I don't understand why if I remove the double braces for the array it does not work anymore.

std::array<std::vector<int>, 2> v = { {1,2}, {3,4} }; // Does not compile

I understand how std::array works and the need for the double braces in general, but as I'm compiling for C++17 I expected brace elision to come into play.

Why is brace elision not applicable here?

like image 326
Svalorzen Avatar asked Nov 14 '18 17:11

Svalorzen


2 Answers

std::array<std::vector<int>, 2> is effectively

struct array {
    std::vector<int> elems[2];
};

elems is a subaggregate just fine. The issue is that per the language rules, if the initializer starts with a { it's always assumed that you aren't eliding braces; instead, {1, 2} is taken as the initializer of the entire subaggregate elems, attempting to initialize its first element with 1 and second element with 2 (this is obviously invalid - you can't convert an integer to a vector - but doesn't affect the interpretation), and {3, 4} is considered the initializer for the thing after elems - and since there are no such thing, it's another error.

Initializing the first element with something that's not a braced-init-list is sufficient to trigger brace elision:

std::array<std::vector<int>, 2> v = { std::vector<int>{1,2}, {3,4} }; 

Note that from a specification perspective, the library doesn't guarantee initialization of std::array<T, N> from anything other than another std::array<T, N> or a list of "up to N elements whose types are convertible to T". This notably excludes braced-init-lists because they have no type, and actually also disallows "double braces" because that's just a special case of having a single element that is a braced-init-list .

This is an area where we may have been better off specifying it with code. The core language rules defy easy specification in words and the implementation details will leak out - and have already done so.

like image 87
T.C. Avatar answered Sep 22 '22 01:09

T.C.


As T.C. pointed out my original interpretation was not corret, brace elision is allowed see [dcl.init.aggr]p15:

Braces can be elided in an initializer-list as follows. If the initializer-list begins with a left brace, then the succeeding comma-separated list of initializer-clauses initializes the elements of a subaggregate; it is erroneous for there to be more initializer-clauses than elements. If, however, the initializer-list for a subaggregate does not begin with a left brace, then only enough initializer-clauses from the list are taken to initialize the elements of the subaggregate; any remaining initializer-clauses are left to initialize the next element of the aggregate of which the current subaggregate is an element. ...

but std::array according to array.overview:

An array is an aggregate that can be list-initialized with up to N elements whose types are convertible to T.

which is not the case we have.

like image 42
Shafik Yaghmour Avatar answered Sep 18 '22 01:09

Shafik Yaghmour