Спецификаторы типа заполнителя (начиная с C++11)
Для переменных указывает, что тип объявляемой переменной будет автоматически выведен из её инициализатора.
|
Для функций указывает, что тип возвращаемого значения будет выведен из их операторов return. |
(начиная с C++14) |
|
Для параметров шаблона, не относящихся к типу, указывает, что тип будет выведен из аргумента. |
(начиная с C++17) |
Синтаксис
ограничение-типа (необязательно) auto
|
(1) | (начиная с C++11) | |||||||
ограничение-типа (необязательно) decltype(auto)
|
(2) | (начиная с C++14) | |||||||
| ограничение-типа | — | (начиная с C++20) имя концепта, необязательно уточнённое, за которым может следовать список аргументов шаблона, заключённый в <>
|
decltype(выражение), где выражение это инициализатор или то, что используется в операторах возврата.Заполнитель auto может сопровождаться модификаторами, такими как const или &, которые будут участвовать в выводе типа. Заполнитель decltype(auto) должен быть единственным компонентом объявленного типа. (начиная с C++14)
Объяснение
Спецификатор типа заполнителя может появляться в следующих контекстах:
- в последовательности спецификатора типа переменной:
auto x = выражение;в качестве спецификатора типа. Тип выводится из инициализатора.
Если спецификатором типа заполнителя являетсяautoили ограничение-типаauto(начиная с C++20), тип переменной выводится из инициализатора с использованием правил для вывода аргумента шаблона из вызова функции (для подробностей смотрите вывод аргумента шаблона — другие контексты).
Например, при данномconst auto& i = выражение;, типiв точности совпадёт с типом аргументаuв воображаемом шаблонеtemplate<class U> void f(const U& u), если вызов функцииf(выражение)был скомпилирован. Следовательно,auto&&может быть выведено либо как ссылка lvalue, либо как ссылка rvalue в соответствии с инициализатором, который используется в цикле for на основе диапазона.
Если спецификатор типа заполнителя имеет вид
decltype(auto)или ограничение-типаdecltype(auto)(начиная с C++20), выведенный тип этоdecltype(выражение), гдевыражениеэто инициализатор.(начиная с C++14) Если спецификатор типа заполнителя используется для объявления нескольких переменных, выведенные типы должны совпадать. Например, объявление
auto i = 0, d = 0.0;некорректно, а объявлениеauto i = 0, *p = &i;правильно иautoвыводится какint. - в идентификаторе типа выражения new. Тип выводится из инициализатора. Для
new T инициализатор(гдеTсодержит тип-заполнитель, инициализатор является либо инициализатором в скобках, либо списком инициализаторов в фигурных скобках), типTвыводится, как для переменнойxв придуманном объявленииT x инициализатор;. - (начиная с C++14) в возвращаемом типе функции или лямбда-выражения:
auto& f();. Тип возвращаемого значения выводится из операнда не отброшенного (начиная с C++17) оператора return.
Смотрите функция — вывод типа возвращаемого значения. - (начиная с C++17) в объявлении параметра шаблона не типа:
template<auto I> struct A;. Его тип выводится из соответствующего аргумента.
|
Спецификатор |
(начиная с C++23) |
|
Кроме того,
|
(начиная с C++14) |
Ограничение типаЕсли присутствует ограничение-типа, пусть
Вывод невозможен, если выражение ограничения недопустимо или возвращает |
(начиная с C++20) |
Примечание
До C++11, auto имел семантику спецификатора длительности хранения.
Смешивание auto переменных и функций в одном объявлении, как в auto f() -> int, i = 0; не допускается.
Спецификатор auto также может использоваться с декларатором функции, за которым следует конечный возвращаемый тип, и в этом случае объявленный возвращаемый тип является завершающим возвращаемым типом (который также может быть типом-заполнителем).
auto (*p)() -> int; // объявляет p как указатель на функцию, возвращающую int
auto (*q)() -> auto = p; // объявляет q как указатель на функцию, возвращающую T,
// где T выводится из типа p
|
Спецификатор |
(начиная с C++17) |
|
Ключевое слово |
(ТС концепций) |
| Макрос Тестирования функциональности | Значение | Стандарт | Функциональность |
|---|---|---|---|
__cpp_decltype_auto |
201304L |
(C++14) | decltype(auto)
|
Пример
#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 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 << "до модификации через c2, a = " << a << '\n';
++c2;
std::cout << " после модификации через 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); // подавляет предупреждения о
// "неиспользуемой переменной"
}
Возможный вывод:
до модификации через c2, a = 3
после модификации через c2, a = 4
Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| CWG 1265 | C++11 | спецификатор auto можно использовать для объявления функциис конечным возвращаемым типом и определения переменной в одном операторе объявления |
запрещено |
| CWG 1346 | C++11 | список выражений в скобках не может быть присвоен автоматической переменной | разрешено |
| CWG 1347 | C++11 | объявление со спецификатором auto может определять две переменныес типами T и std::initializer_list<T> соответственно
|
запрещено |
| CWG 1852 | C++14 | спецификатор auto в decltype(auto) также был заполнителем
|
не заполнитель в данном случае |