auto[]

Document #: D????R0
Date: 2024-07-15
Project: Programming Language C++
Audience: Evolution Incubator
Reply-to: Danil Sidoruk
<>

1 Introduction

Explicit prohibition on array types in types containing a placeholder type seems artificial and inconsistent with other core features, so this paper proposes removing it.

2 Motivation

In the current working draft [N4981] there is no way to deduce the type of the array. However, we can deduce the type of the initializer list (godbolt):

#include <initializer_list>

int main() {
    auto x = {1, 2, 3, 4, 5}; // x has type of std::initializer_list<int>
}

As well as we can use template to deduce the array element’s type in the function’s parameter (godbolt):

auto f(auto(&&arr)[]) { return arr[0]; }

int main() {
    f({1, 2, 3, 4, 5});
}

But, for reasons unknown, we can’t deduce the placeholder type in the array type itself (godbolt):

int main() {
    auto arr[] = {1, 2, 3, 4, 5}; // IF
}

This restriction was introduced intentionally in [N1984] page 6:

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 * ?

However, I find these arguments unconvincing. Array-to-pointer conversions has never been a problem in the context of placeholder type deduction. If we get a compilation error in the following program, then why should we think about it in the context of auto?

int x[5];
/* ~auto~ */ int y[5] = x; // IF, and it's OK

auto is a placeholder type and nothing more. If we allowed auto y[5] = x, it would mean that auto has acquired an additional meaning that changes the initialization rules. Answering to the questions above, we propose enabling auto x[] = { a, b, c }, but we don’t propose allowing either auto y[] = x or auto y[5] = x from the examples above.

We believe that the current status quo is inconsistent with others core features, as it creates an unexplainable exception to the general rule of placeholder type deduction for arrays. We propose removing this exception allowing deduction of a placeholder type in array types.

3 Design

The only changes we propose is removing the exception prohibiting array types in types containing a placeholder type. We don’t propose any changes to the existing rules for template arguments deduction from function call (13.10.3.2 [temp.deduct.call]), which are the heart of placeholder type deduction, or anywhere else.

int main() {
    auto x0[] = {1, 'a'}; // IF (1)
//  auto x0 = {1, 'a'};   // IF

    int x1[] = {1, 2};
    auto x2[] {x1}; // OK (2)

    auto x3[] (1, 2); // OK (3)
//  int x3[] (1, 2);  // OK

    auto x4[][] = {{1, 2}, {3, 4}}; // IF (4)

    auto x5[3] = {1, 2}; // OK (5)
}
  1. The declaration is IF, because of the rules for template arguments deduction from function call (13.10.3.2 [temp.deduct.call]). This behavior is consistent with the behavior of type deduction in the case of std::initializer_list, cause arrays and std::initializer_list are sharing the same rules.
  2. The declaration is OK, cause it’s common aggregate initialization syntax for array types, the type of x2 is int*[1]
  3. The declaration is OK, cause it’s common direct initialization syntax for array types, the type of x3 is int[2]
  4. The declaration is IF, because an array cannot have an incomplete element type (auto[] is an incomplete type).
  5. The declaration is OK, because aggregate initialization rules do not require explicit initialization of all elements, (9.4.2 [dcl.init.aggr]) the type of x5 is int[3].

4 Wording

Modify 9.2.9.7.2 [dcl.type.auto.deduct] paragraph 2 as follows:

2 A type T containing a placeholder type, and a corresponding initializer-clause E, are determined as follows:

T shall not be an array type.

Modify 9.2.9.7.2 [dcl.type.auto.deduct] paragraph 3 as follows:

If the placeholder-type-specifier is of the form type-constraintopt auto, the deduced type T′ replacing T is determined using the rules for template argument deduction. If the initialization is copy-list-initialization and the type T is not an array type, a declaration of std::initializer_list shall precede (6.5.1 [basic.lookup.general]) the placeholder-type-specifier. Obtain P from T by replacing the occurrences of type-constraintopt auto either with a new invented type template parameter U or, if the initialization is copy-list-initialization and the type T is not an array type, with std::initializer_list<U>. Deduce a value for U using the rules of template argument deduction from a function call (13.10.3.2 [temp.deduct.call]), where P is a function template parameter type and the corresponding argument is E, except that if P is an array type, P& is used in place of P as a function template parameter type. If the deduction fails, the declaration is ill-formed. Otherwise, T′ is obtained by substituting the deduced U into P.

Modify 9.3.4.5 [dcl.array] paragraph 8 as follows:

An array bound may also be omitted when an object (but not a non-static data member) of array type is initialized and the declarator is followed by an initializer (9.4 [dcl.init], 11.4 [class.mem], 7.6.1.4 [expr.type.conv], 7.6.2.8 [expr.new], 9.2.9.7.2 [dcl.type.auto.deduct]).

5 References

[N1984] J. J�rvi, B. Stroustrup, G. Dos Reis. 2006-04-06. Deducing the type of variable from its initializer expression (revision 4).
https://wg21.link/n1984
[N4981] Thomas Köppe. 2024-04-16. Working Draft, Programming Languages — C++.
https://wg21.link/n4981