2157. How does std::array<T,0> initialization work when T is not default-constructible?

Section: 26.3.7.5 [array.zero] Status: Open Submitter: Daryle Walker Opened: 2012-05-08 Last modified: 2016-02-10

Priority: 3

View all other issues in [array.zero].

View all issues with Open status.

Discussion:

Objects of std::array<T,N> are supposed to be initialized with aggregate initialization (when not the destination of a copy or move). This clearly works when N is positive. What happens when N is zero? To continue using an (inner) set of braces for initialization, a std::array<T,0> implementation must have an array member of at least one element, and let default initialization take care of those secret elements. This cannot work when T has a set of constructors and the default constructor is deleted from that set. Solution: Add a new paragraph in 26.3.7.5 [array.zero]:

The unspecified internal structure of array for this case shall allow initializations like:

array<T, 0> a = { };

and said initializations must be valid even when T is not default-constructible.

[2012, Portland: Move to Open]

Some discussion to understand the issue, which is that implementations currently have freedom to implement an empty array by holding a dummy element, and so might not support value initialization, which is surprising when trying to construct an empty container. However, this is not mandated, it is an unspecified implementation detail.

Jeffrey points out that the implication of 26.3.7.1 [array.overview] is that this initialization syntax must be supported by empty array objects already. This is a surprising inference that was not obvious to the room, but consensus is that the reading is accurate, so the proposed resolution is not necessary, although the increased clarity may be useful.

Further observation is that the same clause effectively implies that T must always be DefaultConstructible, regardless of N for the same reasons - as an initializer-list may not supply enough values, and the remaining elements must all be value initialized.

Concern that we are dancing angels on the head of pin, and that relying on such subtle implications in wording is not helpful. We need a clarification of the text in this area, and await wording.

[2015-02 Cologne]

DK: What was the outcome of Portland? AM: Initially we thought we already had the intended behaviour. We concluded that T must always be DefaultConstructible, but I'm not sure why. GR: It's p2 in std::array, "up to N". AM: That wording already implies that "{}" has to work when N is zero. But the wording of p2 needs to be fixed to make clear that it does not imply that T must be DefaultConstructible.

Conclusion: Update wording, revisit later.

[2015-10, Kona Saturday afternoon]

MC: How important is this? Can you not just use default construction for empty arrays?

TK: It needs to degenerate properly from a pack. STL agrees.

JW: Yes, this is important, and we have to make it work.

MC: I hate the words "initialization like".

JW: I'll reword this.

WEB: Can I ask that once JW has reworded this we move it to Review rather than Open?

MC: We'll try to review it in a telecon and hopefully get it to tentatively ready.

STL: Double braces must also work: array<T, 0> a = {{}};.

Jonathan to reword.

Proposed resolution:

This wording is relative to N3376.

Add the following new paragraph between the current 26.3.7.5 [array.zero] p1 and p2:

-1- array shall provide support for the special case N == 0.

-?- The unspecified internal structure of array for this case shall allow initializations like:

array<T, 0> a = { };

and said initializations must be valid even when T is not default-constructible.

-2- In the case that N == 0, begin() == end() == unique value. The return value of data() is unspecified.

-3- The effect of calling front() or back() for a zero-sized array is undefined.

-4- Member function swap() shall have a noexcept-specification which is equivalent to noexcept(true).