List-initialization of a reference to an array of unknown size: is it supposed to deduce the array size?
Asked Answered
R

1

6

The following code compiles fine in Clang and outputs size of int [3] array

#include <iostream>

int main()
{
  const int (&a)[] = { 1, 2, 3 };
  std::cout << sizeof a << std::endl;
}

However, in GCC the declaration compiles fine, but sizeof a doesn't: apparently GCC refuses to "deduce" the array size and ends up with a as a reference to const int [] type, which is incomplete.

What is the intended behavior in such initializations?

9.3.4/3 appears to be the relevant portion of the standard for such cases, but by itself it doesn't seem to answer this conclusively.

Ridley answered 5/4, 2019 at 22:26 Comment(7)
Is const int (&a)[] = ... even valid? The Standard specifies cases where bounds may be omitted (eel.is/c++draft/dcl.array#3) but this holds only for arrays themselves, not for references to arrays.Remark
@Daniel Langr: The text at your link says: "In addition to declarations in which an incomplete object type is allowed, an array bound may be omitted...". This actually happens to be a declaration in which incomplete type is allowed: a reference to an incomplete type is OK in C++. Arrays of unknown bound have been present in C++ as incomplete types since the beginning of times. It is not immediately clear to me though whether it should remain incomplete in this context. Hence the question.Ridley
@AnT: it says: <type of the identifier of D is “array of N T”>. So it doesn't allow a reference type (I'm not sure this is intended, though).Dispense
@geza: Um... No. The wording you quoted refers to the above "additonal" cases, i.e. the ones described after the part I quoted above. It is not in any way related or connected to the aforementioned "declarations in which an incomplete object type is allowed" and does not prohibit them in any way. The wording is perfectly clear there.Ridley
@AnT: I may misunderstand you, or the standard. Here's the full sentence: <the bound is calculated from the number of initial elements (say, N) supplied ([dcl.init.aggr]), and the type of the identifier of D is “array of N T”>. Isn't it the case here? The bound is calculated from the initializer. By "incomplete type allowed", don't they mean cases like "extern Foo a[];" (Foo can be incomplete here)? While in your case, the declared type must be complete, as it is a definition (a reference to an incomplete type is a complete type, as far as I understand).Dispense
@geza: It is quite possible that this text applies to immediate array declarations only and by "incomplete type allowed" they mean cases like extern Foo a[];. But in that case I don't understand why we are even considering that part of the standard in this context. It was Daniel Langr who brought it here in the very first comment. My question is about a reference declaration. If 9.2.3.4/3 is about array declarations, they let's just forget about it, since it is completely irrelevant.Ridley
The relevant part is 9.2.3.2 "References" and there's nothing there that's say that const int (&a)[] is illegal.Ridley
S
1

The standard is not entirely clear on this point, and I think GCC's interpretation is likely to be what WG21 intended, but I am not certain.

The relevant section of the standard is [dcl.array], which describes how to determine the type declared by a declaration in which a declarator contains the array-forming operator []. I quote the relevant part:

An array bound may also be omitted when the declarator is followed by an initializer (11.6) or when a declarator for a static data member is followed by a brace-or-equal-initializer (12.2). In both cases the bound is calculated from the number of initial elements (say, N) supplied (11.6.1), and the type of the identifier of D is “array of N T”.

It's not entirely whether this only applies to a declaration of an array itself, or whether it should also apply in the case of a reference to an array, since [dcl.array] must be recursively consulted when interpreting [dcl.ref] (which describes the & and && operators). However, I think that the latter interpretation should be rejected, as we would not expect the initializer to result in a bound being deduced when the [] is buried deeper in the declarator. To wit, consider the contrived example:

int (*a[1])(const int (&)[]) = {0};

Here GCC and Clang agree, and I think common sense agrees also, that the type of a is int (*[1])(const int (&)[]), and not int (*[1])(const int (&)[1]): the fact that a possesses an initializer does not cause the inner array bound to be deduced.

Based on this, I would argue that GCC is correct in not deducing the array bound in your code, so that a has type const int (&)[].

Superstitious answered 5/4, 2019 at 23:13 Comment(5)
Can you please read the comments under the question? Isn't it the case, that const int (&a)[] = ... is ill-formed? Because they say: <type of the identifier of D is “array of N T”>, so reference to array is not allowed.Dispense
@geza: You seem to be contradicting yourself. In your most recent comments you seemed to suggest that 9.2.3.4 applies to array declarations only. But this is not an array declaration. It is a reference declaration. Why do you keep quoting that wording, which is clearly not applicable to const int (&a)[] declaration (regardless of how one interprets the scope of 9.2.3.4)?Ridley
@Dispense I'm afraid I don't understand what you're saying at all.Superstitious
@AnT: If we ignore 9.2.3.4 completely (because this is a reference declaration), then what specifies that the object a references to has a size of 3?Dispense
@geza: The rules of reference initialization (linked in my question), say that in order to initialize that reference we have to generate a prvalue of the referenced type. This prvalue's type no longer contains a reference in it. The question basically boils down to whether in this case its type is already "frozen" as an incomplete array type const int[] or it still retains its ability to acquire a specific size const int[3] from the initializer. The wording, taken literally, seems to suggest that the type is supposed to be "frozen" (i.e. GCC is correct).Ridley

© 2022 - 2024 — McMap. All rights reserved.