Files
2025-10-25 03:02:53 +03:00

27 KiB
Raw Permalink Blame History

[dcl.spec.auto]

9 Declarations [dcl]

9.2 Specifiers [dcl.spec]

9.2.9 Type specifiers [dcl.type]

9.2.9.7 Placeholder type specifiers [dcl.spec.auto]

9.2.9.7.1 General [dcl.spec.auto.general]

placeholder-type-specifier:
type-constraintopt auto
type-constraintopt decltype ( auto )

1

#

A placeholder-type-specifier designates a placeholder type that will be replaced later, typically by deduction from an initializer.

2

#

The type of a parameter-declaration of a

function declaration ([dcl.fct]),

lambda-expression ([expr.prim.lambda]), or

template-parameter ([temp.param])

can be declared using a placeholder-type-specifier of the formtype-constraintopt auto.

The placeholder type shall appear as one of the decl-specifiers in the decl-specifier-seq or as one of the type-specifiers in a trailing-return-type that specifies the type that replaces such a decl-specifier (see below); the placeholder type is a generic parameter type placeholder of the function declaration,lambda-expression, ortemplate-parameter, respectively.

[Note 1:

Having a generic parameter type placeholder signifies that the function is an abbreviated function template ([dcl.fct]) or the lambda is a generic lambda ([expr.prim.lambda]).

— end note]

3

#

A placeholder type can appear in the decl-specifier-seq for a function declarator that includes a trailing-return-type ([dcl.fct]).

4

#

A placeholder type can appear in the decl-specifier-seq or type-specifier-seq in the declared return type of a function declarator that declares a function; the return type of the function is deduced from non-discarded return statements, if any, in the body of the function ([stmt.if]).

5

#

The type of a variable declared using a placeholder type is deduced from its initializer.

This use is allowed in an initializing declaration ([dcl.init]) of a variable.

The placeholder type shall appear as one of thedecl-specifiers in the decl-specifier-seq or as one of thetype-specifiers in a trailing-return-type that specifies the type that replaces such a decl-specifier; the decl-specifier-seq shall be followed by one or moredeclarators, each of which shall be followed by a non-emptyinitializer.

[Example 1: auto x = 5; // OK, x has type intconst auto v = &x, u = 6; // OK, v has type const int, u has type const intstatic auto y = 0.0; // OK, y has type doubleauto int r; // error: auto is not a storage-class-specifierauto f() -> int; // OK, f returns intauto g() { return 0.0; } // OK, g returns doubleauto (*fp)() -> auto = f; // OKauto h(); // OK, h's return type will be deduced when it is defined — end example]

The auto type-specifier can also be used to introduce a structured binding declaration ([dcl.struct.bind]).

6

#

A placeholder type can also be used in the type-specifier-seq of the new-type-id or in the type-id of anew-expression ([expr.new]).

In such a type-id, the placeholder type shall appear as one of the type-specifiers in the type-specifier-seq or as one of the type-specifiers in a trailing-return-type that specifies the type that replaces such a type-specifier.

7

#

The auto type-specifier can also be used as the simple-type-specifier in an explicit type conversion (functional notation) ([expr.type.conv]).

8

#

A program that uses a placeholder type in a context not explicitly allowed in [dcl.spec.auto] is ill-formed.

9

#

If the init-declarator-list contains more than oneinit-declarator, they shall all form declarations of variables.

The type of each declared variable is determined by placeholder type deduction, and if the type that replaces the placeholder type is not the same in each deduction, the program is ill-formed.

[Example 2: auto x = 5, *y = &x; // OK, auto is intauto a = 5, b = { 1, 2 }; // error: different types for auto — end example]

10

#

If a function with a declared return type that contains a placeholder type has multiple non-discarded return statements, the return type is deduced for each such return statement.

If the type deduced is not the same in each deduction, the program is ill-formed.

11

#

If a function with a declared return type that uses a placeholder type has no non-discarded return statements, the return type is deduced as though from areturn statement with no operand at the closing brace of the function body.

[Example 3: auto f() { } // OK, return type is voidauto* g() { } // error: cannot deduce auto* from void() — end example]

12

#

An exported function with a declared return type that uses a placeholder type shall be defined in the translation unit containing its exported declaration, outside the private-module-fragment (if any).

[Note 2:

The deduced return type cannot have a name with internal linkage ([basic.link]).

— end note]

13

#

If a variable or function with an undeduced placeholder type is named by an expression ([basic.def.odr]), the program is ill-formed.

Once a non-discarded return statement has been seen in a function, however, the return type deduced from that statement can be used in the rest of the function, including in otherreturn statements.

[Example 4: auto n = n; // error: n's initializer refers to nauto f();void g() { &f; } // error: f's return type is unknownauto sum(int i) {if (i == 1)return i; // sum's return type is intelsereturn sum(i-1)+i; // OK, sum's return type has been deduced} — end example]

14

#

A result binding never has an undeduced placeholder type ([dcl.contract.res]).

[Example 5: auto f() post(r : r == 7) // OK{return 7;} — end example]

15

#

Return type deduction for a templated function with a placeholder in its declared type occurs when the definition is instantiated even if the function body contains a return statement with a non-type-dependent operand.

[Note 3:

Therefore, any use of a specialization of the function template will cause an implicit instantiation.

Any errors that arise from this instantiation are not in the immediate context of the function type and can result in the program being ill-formed ([temp.deduct]).

— end note]

[Example 6: template auto f(T t) { return t; } // return type deduced at instantiation timetypedef decltype(f(1)) fint_t; // instantiates f to deduce return typetemplate auto f(T* t) { return *t; }void g() { int (p)(int) = &f; } // instantiates both fs to determine return types,// chooses second — end example]

16

#

If a function or function template F has a declared return type that uses a placeholder type, redeclarations or specializations of F shall use that placeholder type, not a deduced type; otherwise, they shall not use a placeholder type.

[Example 7: auto f();auto f() { return 42; } // return type is intauto f(); // OKint f(); // error: auto and int don't matchdecltype(auto) f(); // error: auto and decltype(auto) don't matchtemplate auto g(T t) { return t; } // #1template auto g(int); // OK, return type is inttemplate char g(char); // error: no matching templatetemplate<> auto g(double); // OK, forward declaration with unknown return typetemplate T g(T t) { return t; } // OK, not functionally equivalent to #1template char g(char); // OK, now there is a matching templatetemplate auto g(float); // still matches #1void h() { return g(42); } // error: ambiguoustemplate struct A {friend T frf(T);};auto frf(int i) { return i; } // not a friend of Aextern int v;auto v = 17; // OK, redeclares vstruct S {static int i;};auto S::i = 23; // OK — end example]

17

#

A function declared with a return type that uses a placeholder type shall not be virtual ([class.virtual]).

18

#

A function declared with a return type that uses a placeholder type shall not be a coroutine ([dcl.fct.def.coroutine]).

19

#

An explicit instantiation declaration does not cause the instantiation of an entity declared using a placeholder type, but it also does not prevent that entity from being instantiated as needed to determine its type.

[Example 8: template auto f(T t) { return t; }extern template auto f(int); // does not instantiate fint (*p)(int) = f; // instantiates f to determine its return type, but an explicit// instantiation definition is still required somewhere in the program — end example]

9.2.9.7.2 Placeholder type deduction [dcl.type.auto.deduct]

1

#

Placeholder type deduction is the process by which a type containing a placeholder type is replaced by a deduced type.

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.

3

#

If the placeholder-type-specifier is of the formtype-constraintopt auto, the deduced typeT′ replacing T is determined using the rules for template argument deduction.

If the initialization is copy-list-initialization, a declaration of std::initializer_list shall precede ([basic.lookup.general]) the placeholder-type-specifier.

Obtain P fromT by replacing the occurrence oftype-constraintopt auto either with a new invented type template parameter U or, if the initialization is copy-list-initialization, withstd::initializer_list.

Deduce a value for U using the rules of template argument deduction from a function call, where P is a function template parameter type and the corresponding argument is E.

If the deduction fails, the declaration is ill-formed.

Otherwise, T′ is obtained by substituting the deduced U into P.

[Example 1: auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_listauto x2 = { 1, 2.0 }; // error: cannot deduce element typeauto x3{ 1, 2 }; // error: not a single elementauto x4 = { 3 }; // decltype(x4) is std::initializer_listauto x5{ 3 }; // decltype(x5) is int — end example]

[Example 2: const auto &i = expr;

The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template:template void f(const U& u);

— end example]

4

#

If the placeholder-type-specifier is of the formtype-constraintopt decltype(auto),T shall be the placeholder alone.

The type deduced for T is determined as described in [dcl.type.decltype], as thoughE had been the operand of the decltype.

[Example 3: int i;int&& f();auto x2a(i); // decltype(x2a) is intdecltype(auto) x2d(i); // decltype(x2d) is intauto x3a = i; // decltype(x3a) is intdecltype(auto) x3d = i; // decltype(x3d) is intauto x4a = (i); // decltype(x4a) is intdecltype(auto) x4d = (i); // decltype(x4d) is int&auto x5a = f(); // decltype(x5a) is intdecltype(auto) x5d = f(); // decltype(x5d) is int&&auto x6a = { 1, 2 }; // decltype(x6a) is std::initializer_listdecltype(auto) x6d = { 1, 2 }; // error: { 1, 2 } is not an expressionauto x7a = &i; // decltype(x7a) is intdecltype(auto)*x7d = &i; // error: declared type is not plain decltype(auto)auto f1(int x) -> decltype((x)) { return (x); } // return type is int&auto f2(int x) -> decltype(auto) { return (x); } // return type is int&& — end example]

5

#

For a placeholder-type-specifier with a type-constraint, the immediately-declared constraint ([temp.param]) of the type-constraint for the type deduced for the placeholder shall be satisfied.