10 Declarations [dcl.dcl]

10.1 Specifiers [dcl.spec]

10.1.7 Type specifiers [dcl.type]

10.1.7.4 The auto specifier [dcl.spec.auto]

Символыauto и используются для обозначения типа заполнителя, который позже будет заменен путем вычитания из инициализатора. Также используется , чтобы ввести тип функции , имеющий или чтобы показать , что лямбда является общим лямбда ( ). Также используется для введения .decltype(auto) type-specifiersauto type-specifiertrailing-return-type[expr.prim.lambda.closure]auto type-specifierstructured binding declaration

Тип заполнитель может появиться с функцией описателем в decl-specifier-seq, type-specifier-seq, conversion-function-id, или trailing-return-type, в любом контексте , где такой описатель является действительным. Если декларатор функции включает в себя trailing-return-type([dcl.fct]), который trailing-return-typeуказывает объявленный тип возвращаемого значения функции. В противном случае декларатор функции должен объявить функцию. Если объявленный тип возвращаемого значения функции содержит тип-заполнитель, тип возвращаемого значения функции выводится из неотброшенныхreturn операторов, если таковые имеются, в теле функции ([stmt.if]).

Если появляется как один из в a из a , лямбда - это ( ). [auto type-specifierdecl-specifiers decl-specifier-seqparameter-declarationlambda-expressiongeneric lambda [expr.prim.lambda.closure]Example:

auto glambda = [](int i, auto a) { return i; };     // OK: a generic lambda

end example]

Тип переменной, объявленной с использованиемauto илиdecltype(auto) , выводится из ее инициализатора. Это использование разрешено в инициализирующем объявлении ([dcl.init]) переменной. auto илиdecltype(auto) должно отображаться как одно из decl-specifiers в, decl-specifier-seqа за ним decl-specifier-seq должно следовать одно или несколько declarators, за каждым из которых должно следовать непустое значение initializer. В initializerформе

( expression-list )

это expression-listдолжно быть единым assignment-expression. [Example:

auto x = 5;                     // OK: x has type int
const auto *v = &x, u = 6;      // OK: v has type const int*, u has type const int
static auto y = 0.0;            // OK: y has type double
auto int r;                     // error: auto is not a storage-class-specifier
auto f() -> int;                // OK: f returns int
auto g() { return 0.0; }        // OK: g returns double
auto h();                       // OK: h's return type will be deduced when it is defined

end example]

Тип заполнитель также может быть использован в type-specifier-seqв new-type-idили type-idв А new-expression и , как decl-specifier из parameter-declarationdecl-specifier-seq в template-parameter.

Программа, которая используетauto илиdecltype(auto) в контексте, явно не разрешенном в этом разделе, имеет неправильный формат.

Если init-declarator-listсодержит более одного init-declarator, все они должны формировать объявления переменных. Тип каждой объявленной переменной определяетсяplaceholder type deduction, и если тип, заменяющий тип заполнителя, не является одинаковым при каждом выводе, программа имеет неправильный формат.

[Example:

auto x = 5, *y = &x;            // OK: auto is int
auto a = 5, b = { 1, 2 };       // error: different types for auto

end example]

Если функция с объявленным типом возвращаемого значения, содержащим тип заполнителя, имеет несколько неотброшенныхreturn операторов, тип возвращаемого значения выводится для каждого такогоreturn оператора. Если выведенный тип не является одинаковым в каждом из выводов, программа сформирована неправильно.

Если функция с объявленным типом возврата, которая использует тип заполнителя, не имеет неотброшенныхreturn операторов, тип возвращаемого значения выводится, как если бы из return оператора без операнда в закрывающей фигурной скобке тела функции. [Example:

auto  f() { }                   // OK, return type is void
auto* g() { }                   // error, cannot deduce auto* from void()

end example]

Если для определения типа выражения требуется тип сущности с невыявленным типом заполнителя, программа имеет неправильный формат. Однако после того, как неотброшенныйreturn оператор был замечен в функции, тип возвращаемого значения, выведенный из этого оператора, можно использовать в остальной части функции, в том числе в других return операторах. [Example:

auto n = n;                     // error, n's type is unknown
auto f();
void g() { &f; }                // error, f's return type is unknown
auto sum(int i) {
  if (i == 1)
    return i;                   // sum's return type is int
  else
    return sum(i-1)+i;          // OK, sum's return type has been deduced
}

end example]

Вывод типа возвращаемого значения для шаблона функции с заполнителем в объявленном типе происходит при создании экземпляра определения, даже если тело функции содержитreturn оператор с операндом, не зависящим от типа. [ Note: Следовательно, любое использование специализации шаблона функции вызовет неявное создание экземпляра. Любые ошибки, возникающие из-за этого экземпляра, не относятся к непосредственному контексту типа функции и могут привести к неправильному формату программы ([temp.deduct]). ] [end noteExample:

template <class T> auto f(T t) { return t; }    // return type deduced at instantiation time
typedef decltype(f(1)) fint_t;                  // instantiates f<int> to deduce return type
template<class T> auto f(T* t) { return *t; }
void g() { int (*p)(int*) = &f; }               // instantiates both fs to determine return types,
                                                // chooses second

end example]

Повторные объявления или специализации функции или шаблона функции с объявленным типом возврата, который использует тип заполнителя, также должны использовать этот заполнитель, а не выводимый тип. [Example:

auto f();
auto f() { return 42; }                         // return type is int
auto f();                                       // OK
int f();                                        // error, cannot be overloaded with auto f()
decltype(auto) f();                             // error, auto and decltype(auto) don't match

template <typename T> auto g(T t) { return t; } // #1
template auto g(int);                           // OK, return type is int
template char g(char);                          // error, no matching template
template<> auto g(double);                      // OK, forward declaration with unknown return type

template <class T> T g(T t) { return t; }       // OK, not functionally equivalent to #1
template char g(char);                          // OK, now there is a matching template
template auto g(float);                         // still matches #1

void h() { return g(42); }                      // error, ambiguous

template <typename T> struct A {
  friend T frf(T);
};
auto frf(int i) { return i; }                   // not a friend of A<int>

end example]

Функция, объявленная с возвращаемым типом, использующим тип-заполнитель, не должна бытьvirtual.

Anexplicit instantiation declaration не вызывает создание экземпляра сущности, объявленной с использованием типа заполнителя, но также не предотвращает создание экземпляра этой сущности по мере необходимости для определения ее типа. [Example:

template <typename T> auto f(T t) { return t; }
extern template auto f(int);    // does not instantiate f<int>
int (*p)(int) = f;              // instantiates f<int> to determine its return type, but an explicit
                                // instantiation definition is still required somewhere in the program

end example]

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

Placeholder type deduction - это процесс, с помощью которого тип, содержащий тип-заполнитель, заменяется выведенным типом.

Тип,T содержащий тип заполнителя и соответствующий инициализаторe, определяются следующим образом:

  • для неотброшенногоreturn оператора, который встречается в функции, объявленной с возвращаемым типом, который содержит тип-заполнитель, T является объявленным возвращаемым типом иe является операндомreturn оператора. Если уreturn оператора нет операнда, тоe естьvoid();

  • для переменной, объявленной с типом, содержащим тип-заполнитель, T является объявленным типом переменной иe инициализатором. Если инициализация - это инициализация с прямым списком, инициализатор должен braced-init-list содержать только один элемент assignment-expression иe является assignment-expression;

  • для параметра шаблона, не являющегося типом, объявленного с типом, содержащим тип-заполнитель, T является объявленным типом параметра шаблона, не являющимся типом, иe является соответствующим аргументом шаблона.

В случаеreturn заявления, без операнда или с операндом типаvoid, T должны быть либо decltype(auto) илиcvauto.

Если вывод предназначен дляreturn оператора иe представляет собой braced-init-list([dcl.init.list]), программа сформирована неправильно.

Если заполнитель - это , замена выведенного типа определяется с использованием правил вывода аргументов шаблона. Получить от путем замены вхождений либо на новый параметр шаблона изобретенного типа, либо, если инициализация - инициализация списка-копирования, на . Выведите значение для использования правил , где - тип параметра шаблона функции, а соответствующий аргумент - . Если вычет не удается, декларация имеет неверный формат. В противном случае получается заменой выведенного на . [auto type-specifierT' TP T auto U std​::​initializer_­list<U>U template argument deduction from a function callP eT' U PExample:

auto x1 = { 1, 2 };             // decltype(x1) is std​::​initializer_­list<int>
auto x2 = { 1, 2.0 };           // error: cannot deduce element type
auto x3{ 1, 2 };                // error: not a single element
auto x4 = { 3 };                // decltype(x4) is std​::​initializer_­list<int>
auto x5{ 3 };                   // decltype(x5) is int

end example]

[Example:

const auto &i = expr;

Типi - это выведенный тип параметраu при вызовеf(expr) следующего шаблона придуманной функции:

template <class U> void f(const U& u);

end example]

Если заполнителем является , должен быть только заполнитель. Тип, выведенный для , определяется, как описано в , как если бы он был операндом . [decltype(auto) type-specifierT T [dcl.type.simple]e decltypeExample:

int i;
int&& f();
auto           x2a(i);          // decltype(x2a) is int
decltype(auto) x2d(i);          // decltype(x2d) is int
auto           x3a = i;         // decltype(x3a) is int
decltype(auto) x3d = i;         // decltype(x3d) is int
auto           x4a = (i);       // decltype(x4a) is int
decltype(auto) x4d = (i);       // decltype(x4d) is int&
auto           x5a = f();       // decltype(x5a) is int
decltype(auto) x5d = f();       // decltype(x5d) is int&&
auto           x6a = { 1, 2 };  // decltype(x6a) is std​::​initializer_­list<int>
decltype(auto) x6d = { 1, 2 };  // error, { 1, 2 } is not an expression
auto          *x7a = &i;        // decltype(x7a) is int*
decltype(auto)*x7d = &i;        // error, declared type is not plain decltype(auto)

end example]