C++ Logo

std-discussion

Advanced search

Re: Rationale for no auto arrays?

From: will wray <wjwray_at_[hidden]>
Date: Thu, 24 Feb 2022 10:46:30 -0400
> Why can't we write:
> auto arr[2] = { 1, 2 };

The proposal that revived "auto" decided to pass on it.
wg21.link/n1984 (ominous number!)
 -----
 Deducing the type of variable from its initializer expression
  ("auto", revision 4). Approved. June 2006.
 J. Jarvi, B. Stroustrup, G. Dos Reis: N1984==06-0054.
 ...
 Section 8.3.4 arrays [dcl.ptr]
> ... if the type of the identifier of D contains the auto type
       deduction type-specifier, the program is ill-formed...
  Currently, the change is thus to ban the use of auto with arrays.
  This is due to arrays decaying to pointers automatically.
  For example:
       int x[5];
       auto y[5] = x;
  Here, expression x would decay to a pointer, and would not match
  the type “auto y[5]”. Note that depending on the work on initializers
  we may wish to revisit this part.
  For example, we may wish to enable
       auto x[] = {a, b, c};
  Also, we can debate whether the following should be allowed:
       int x[5];
       auto y[] = x; // would this be allowed and y : int * ?
-----

I don't know if it was ever debated. A lost opportunity IMO.
The supposed issue of eager decay to pointer is now gone
and would've been easily fixed at the time - presumably the
"work on initializers" was 'uniform initialization' - we could
have had auto[] in C++11.

At some point the restrictive wording moved to [dcl.array] p4
> U is called the array element type; this type shall not be
> a placeholder type...

Then, just before C++20 there was a relaxation in use of auto
for array element type, but only for pointers and references:
CWG defect 2397: auto specifier for pointers and references to arrays
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2397
moved the restriction from [dcl.array] to [dcl.type.auto.deduct] p2:
> A type T containing a placeholder type ... T shall not be an array type.

A couple of years ago there was a proposal to remove the restriction:

  P1997 Relaxing Restrictions on Arrays, wg21.link/P1997
  (C array copy semantics & etc.) R1, January 2020

Where the questioned example from n1984:
       auto y[] = x; // would this be allowed and y : int * ?
is interpreted as array copy-initialization from array RHS (no decay).

The proposal was seen by SG22 Joint C and C++ Liaison
in August 2021 and voted forward to EWG, with the strong
recommendation to gain implementation experience first.

I went ahead and implemented a GCC patch for P1997, and
started work on a Clang patch. I succeeded in implementing
most features, including full auto deduction for array-type RHS,
but couldn't get this seemingly simple case to work - deduction
of array element type from a braced init-list of elements:

> Why can't we write:
> auto arr[2] = { 1, 2 };

I don't know! It's harder to do now than it would've been in 11.
It interacts with uniform initialization as well as changes
such as paren-init of aggregates (C++20 P0960).
It might be a breaking change now, or inconsistent at least.
I'd appreciate the thoughts of language experts.
Let's have that array-related debate, belated!

Conversion to arrays of unknown bound (C++20 P0338)
also sets a precedent for what this might mean:

    int (&&ukb)[] {1,2};

I think that with P0338 this should deduce int(&&)[]
(GCC deduces int(&&)[] but did so before C++20)
(this ICEs some compiler versions, some deduce int(&&)[2].)

With the CWG2397 DR mentioned above, reference initialization
of auto array should work, with lifetime extension:

    auto (&&arr)[2] {1,2};

which is semantically indistinguishable from what we want:

     auto arr[2] = { 1, 2 };
------

Let me give some examples of the P1997 relaxations.
P1997 allows copy-init from array RHS:

    T arr[] = {1,2};

    auto c1[] = arr; // P1997 auto deduction, from unbraced array
    auto c2[] = {arr}; // P1997 auto deduction, indirect braced array
    auto c3[]{arr}; // P1997 auto deduction, from direct braced array

My implementation of P1997 gives up on aggregate-init from elements:

    auto il[2] = {1,2}; // error, eagerly tries std::initializer_list<int>
    auto er[2] {1,2}; // error, expects a single element of array type
    auto i1[] {1}; // error, single element 1 isn't an array

based on auto deduction rules for array from braced-init-list:

  * indirect list initialization with auto placeholder will deduce
std::initializer_list,
    (or try to and then hard fail if it can't, e.g. no #include
<initializer_list>), and
  * direct list initialization requires exactly one element (of array
type), (I think)

The single-element case is interpreted as an optionally-braced initializer,
where the single initializer is for the full declared type (auto[] here).
It doesn't fall back to try a common-element-wise deduction.

Consider the classic C initialization of char array from string literal,
and its C++ uniform initialization extension to direct brace-init
(one existing case of array copy-initialization in the language):

    char c[] = "c"; // classic C char array initialization from string
literal
    char e[] = {"e"}; // classic C, indirect init from optionally-braced
init
    char p[] ("p"); // classic (but looks like P0960 paren-init, eh)
    char d[] {"d"}; // uniform C++, direct init from single braced char
array

    auto cstr[] = "P1997"; // proposed to deduce char[6] and copy-init
    auto a[]{"P1997"}; // same
    auto b[] = a; // proposed array copy-init (from non-literal)

    auto dk2p = a; // classic decay to pointer then copy-init pointer

Now, imagine extending this to nested C arrays, with optionally elided
braces in the init-list, with different numbers of elements in sub-lists,
with both bare elements and sub-array elements (P1997), and so on.
That's hard enough to do with a concrete type specified.
Stepping into general aggregate initialization is aggravating,
like descending down Dante's levels...
P0960 made prospects worse.
Array paradise, lost?
------

Anyway, for the motivating question:

> My motivation is wanting to initialize a fixed-size array with
> expressions which return a complex type I would rather not
> have to specify explicitly.

std::array CTAD (C++17) or std::to_array (C++20) allow similar
(as Peter hinted):

    #include <array>

    std::array arr = {1,2};
    auto autarr = std::array{1,2};
    auto fltarr = std::array<float>{1,2}; // compile fail

    auto stdarr = std::to_array({1,2});
    auto intarr = std::to_array<int>({1,2});
    auto nularr = std::to_array<int>({}); // compile fail
    auto sizarr = std::to_array<2>({1,2}); // compile fail

There are arguments against std::array as the solution -
c.f. https://quuxplusone.github.io/blog/2020/08/06/array-size

There's no partial CTAD (yet) so you have to supply std::array either
no template parameters or all - element type and size parameters.

C++20 to_array can also deduce both and also allows to just specify the
element type. It doesn't allow to specify just the array size alone (yet)
(to do so needs another overload with the size template parameter first)
and it currently fails if given empty braces (there's a fix incoming).

 ----

Given the above, it'd still be useful to have auto deduction of element type
and / or size in a C array declaration, without any templates in sight:

     auto arr[] = { 1, 2 };

So seemingly simple a thing to ask...


On Wed, Feb 23, 2022 at 5:04 AM Peter Sommerlad (C++) via Std-Discussion <
std-discussion_at_[hidden]> wrote:

> use std::array
>
> Eyal Rozenberg via Std-Discussion wrote on 23.02.22 01:13:
> > tl;dr: Why can't we write:
> >
> > auto arr[2] = { 1, 2 };
> >
> > and have that compile?
> >
> > --------
> >
> > Longer version:
> >
> > When the auto keyword was repurposed for C++11, and perhaps in later
> > discussions about expanding its use - was the possibility of using auto
> > for the types of arrays discussed? If so, can I get a reference to such
> > discussion, or to papers accepted/rejected which discuss this?
> >
> > I don't quite see why we these statement:
> >
> > auto my_initializer_list = { 1, 2 };
> > int arr[2] = { 1, 2 };
> >
> > should compile (the former in C++17), but
> >
> > auto arr[2] = { 1, 2 };
> >
> > should not. And this seems doubly strange, seeing how we can use all of:
> >
> > const auto
> > auto&
> > auto*
> >
> > in variable definitions. Why is pointerification ok, but arrayfication
> > isn't?
> >
> > Eyal
> >
> > PS - My motivation is wanting to initialize a fixed-size array with
> > expressions which return a complex type I would rather not have to
> > specify explicitly.
> >
>
>
> --
> Peter Sommerlad
>
> Better Software: Consulting, Training, Reviews
> Modern, Safe & Agile C++
>
> peter.cpp_at_[hidden]
> +41 79 432 23 32
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>

Received on 2022-02-24 14:46:42