Namespaces
Variants

Placeholder type specifiers (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const / volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Спецификатор типа-заполнителя обозначает тип-заполнитель , который будет заменён позднее, обычно путём вывода из инициализатора .

Содержание

Синтаксис

type-constraint  (optional) auto (1)
type-constraint  (optional) decltype(auto) (2) (since C++14)
type-constraint - (since C++20) имя концепта , опционально квалифицированное, опционально сопровождаемое списком шаблонных аргументов в <>
1) Тип выводится с использованием правил для template argument deduction .
2) Тип определяется как decltype(expr) , где expr представляет собой инициализатор или операнды, используемые в операторах return .

Заполнитель auto может сопровождаться модификаторами, такими как const или & , которые будут участвовать в выводе типа. Заполнитель decltype ( auto ) должен быть единственным компонентом объявленного типа. (начиная с C++14)

Если присутствует type-constraint , пусть T будет типом, выведенным для placeholder, тогда type-constraint вводит constraint expression следующим образом:

  • Если type-constraint имеет вид Concept<A 1 , ..., A n > , тогда constraint expression будет Concept<T, A 1 , ..., A n > ;
  • в противном случае ( type-constraint имеет вид Concept без списка аргументов), constraint expression будет Concept<T> .

Вывод завершается неудачей, если constraint expression является недопустимым или возвращает false .

(since C++20)

Объяснение

Заполнитель спецификатора типа может появляться в следующих контекстах:

Объявления параметров

В следующих объявлениях параметров тип объявленного параметра может быть синтаксиса (1) :

  • Если параметр лямбда-выражения имеет тип-заполнитель, лямбда-выражение является обобщённым лямбда-выражением.
(since C++14)
(since C++17)
(since C++20)

Объявления функций

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

Замещающий тип может появляться в спецификаторах объявления или спецификаторах типа в объявленном возвращаемом типе декларатора функции . Вывод возвращаемого типа будет применён в этом случае.

(начиная с C++14)
auto f() -> int; // OK: f возвращает int
auto g() { return 0.0; } // OK начиная с C++14: g возвращает double
auto h(); // OK начиная с C++14: возвращаемый тип h будет выведен при его определении

Объявления переменных

Тип переменной, объявленной с использованием типа-заполнителя, выводится из её инициализатора . Такое использование разрешено в инициализирующем объявлении переменной.

Замещающий тип может появляться только как один из спецификаторов объявления в последовательности спецификаторов объявления или как один из спецификаторов типа в завершающем возвращаемом типе, который указывает тип, заменяющий такой спецификатор объявления. В этом случае объявление должно объявлять как минимум одну переменную, и каждая переменная должна иметь непустой инициализатор.

// "auto" в спецификаторах объявления
auto x = 5; // OK: x имеет тип int
const auto *v = &x, u = 6; // OK: v имеет тип const int*, u имеет тип const int
static auto y = 0.0; // OK: y имеет тип double
auto f() -> int;
auto (*fp)() -> auto = f; // OK: "auto" в конце возвращаемого типа
                          // может быть выведен из f

Объявления структурированных привязок

Спецификатор auto может использоваться в объявлении структурированной привязки .

(начиная с C++17)

new выражения

Заполнитель типа может использоваться в последовательности спецификаторов типа type-id для new expression . В таком type-id заполнитель типа должен появляться как один из спецификаторов типа в последовательности спецификаторов типа или как завершающий возвращаемый тип, который определяет тип, заменяющий такой спецификатор типа.

Функциональное приведение типа

Спецификатор типа auto может использоваться как спецификатор типа для функционального приведения типа .

(начиная с C++23)

Примечания

До C++11, auto имел семантику спецификатора продолжительности хранения .

Программа, использующая тип-заполнитель в контексте, не указанном явно выше, является некорректной.

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

  • Некоторые из объявленных сущностей не являются переменными.
  • Тип, заменяющий тип-заполнитель, не совпадает в каждом выводе.
auto f() -> int, i = 0; // Ошибка: объявляет функцию и переменную с "auto"
auto a = 5, b = {1, 2}; // Ошибка: разные типы для "auto"

Если функция или переменная с незамещенным типом-заполнителем используется в выражении, программа является некорректной.

auto v = 1;
auto l = [&]
{
    v++;
    return l;// Ошибка: тип-заполнитель для l не был заменён
};
std::function<void()> p = [&]
{
    v++;
    return p;// OK
};

Ключевое слово auto также может использоваться в квалификаторе вложенного имени. Квалификатор вложенного имени вида auto :: является заполнителем, который заменяется на тип класса или перечисления в соответствии с правилами вывода типа для ограниченного заполнителя .

(concepts TS)
Макрос проверки возможности Значение Стандарт Возможность
__cpp_decltype_auto 201304L (C++14) decltype ( auto )

Ключевые слова

auto , decltype

Пример

#include <iostream>
#include <utility>
template<class T, class U>
auto add(T t, U u) { return t + u; } // возвращаемый тип - это тип operator+(T, U)
// идеальная переадресация вызова функции должна использовать decltype(auto)
// на случай, если вызываемая функция возвращает по ссылке
template<class F, class... Args>
decltype(auto) PerfectForward(F fun, Args&&... args) 
{ 
    return fun(std::forward<Args>(args)...); 
}
template<auto n> // C++17 объявление параметра auto
auto f() -> std::pair<decltype(n), decltype(n)> // auto не может вывести из списка инициализации
{
    return {n, n};
}
int main()
{
    auto a = 1 + 2;          // тип a - int
    auto b = add(1, 1.2);    // тип b - double
    static_assert(std::is_same_v<decltype(a), int>);
    static_assert(std::is_same_v<decltype(b), double>);
    auto c0 = a;             // тип c0 - int, содержит копию a
    decltype(auto) c1 = a;   // тип c1 - int, содержит копию a
    decltype(auto) c2 = (a); // тип c2 - int&, псевдоним a
    std::cout << "before modification through c2, a = " << a << '\n';
    ++c2;
    std::cout << " after modification through c2, a = " << a << '\n';
    auto [v, w] = f<0>(); // декларация структурированной привязки
    auto d = {1, 2}; // OK: тип d - std::initializer_list<int>
    auto n = {5};    // OK: тип n - std::initializer_list<int>
//  auto e{1, 2};    // Ошибка согласно DR n3922, ранее std::initializer_list<int>
    auto m{5};       // OK: тип m - int согласно DR n3922, ранее initializer_list<int>
//  decltype(auto) z = { 1, 2 } // Ошибка: {1, 2} не является выражением
    // auto часто используется для безымянных типов, таких как типы лямбда-выражений
    auto lambda = [](int x) { return x + 3; };
//  auto int x; // допустимо в C++98, ошибка в C++11
//  auto x;     // допустимо в C, ошибка в C++
    [](...){}(c0, c1, v, w, d, n, m, lambda); // подавляет предупреждения "неиспользуемая переменная"
}

Возможный вывод:

before modification through c2, a = 3
 after modification through c2, a = 4

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 1265 C++11 спецификатор auto мог использоваться для объявления функции с завершающим
возвращаемым типом и определения переменной в одном операторе объявления
запрещено
CWG 1346 C++11 заключенный в скобки список выражений не мог быть присвоен переменной auto разрешено
CWG 1347 C++11 объявление со спецификатором auto могло определять две переменные
с типами T и std:: initializer_list < T > соответственно
запрещено
CWG 1852 C++14 спецификатор auto в decltype ( auto ) также был заполнителем не является заполнителем
в этом случае
CWG 1892 C++11 возвращаемый тип type-id указателя на функцию мог быть auto запрещено
CWG 2476 C++11 решение CWG issue 1892 запрещало выведение
возвращаемого типа переменных указателей на функцию из инициализаторов
разрешено
N3922 C++11 прямая инициализация списком для auto выводит std::initializer_list некорректно для более чем одного
элемента, выводит тип элемента
для одного элемента

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 9.2.9.6 Спецификаторы типа-заполнителя [dcl.spec.auto]
  • Стандарт C++20 (ISO/IEC 14882:2020):
  • 9.2.8.5 Спецификаторы типа-заполнителя [dcl.spec.auto]
  • Стандарт C++17 (ISO/IEC 14882:2017):
  • 10.1.7.4 Спецификатор auto [dcl.spec.auto]
  • Стандарт C++14 (ISO/IEC 14882:2014):
  • 7.1.6.4 auto спецификатор [dcl.spec.auto]
  • Стандарт C++11 (ISO/IEC 14882:2011):
  • 7.1.6.4 auto спецификатор [dcl.spec.auto]