Document #: | D????R0 |
Date: | 2024-07-15 |
Project: | Programming Language C++ |
Audience: |
Evolution Incubator |
Reply-to: |
Danil Sidoruk <d.sidoruk@cppmoscow.com> |
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.
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() {
({1, 2, 3, 4, 5});
f}
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 enableauto 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.
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)
}
std::initializer_list
,
cause arrays and std::initializer_list
are sharing the same rules.x2
is int*[1]
x3
is int[2]
auto[]
is an incomplete type).x5
is int[3]
.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 ofstd::initializer_list
shall precede (6.5.1 [basic.lookup.general]) the placeholder-type-specifier. Obtain P from T by replacing the occurrences of type-constraintoptauto
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, withstd::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]).