占位类型说明符 (C++11 起)
占位类型说明符指定了后续会被替换的占位类型,替换的类型通常通过初始化器推导得到。
语法
类型约束 (可选) auto
|
(1) | ||||||||
类型约束 (可选) decltype(auto)
|
(2) | (C++14 起) | |||||||
类型约束 | - | (C++20 起) 概念名,可以有限定,可以后随以 <> 包围的模板实参列表
|
占位符 auto 可伴随如 const 或 &
这样的修饰符,它们参与类型推导。占位符 decltype(auto) 必须是被声明类型的唯一组分。 (C++14 起)
如果类型约束 存在,令
如果约束表达式无效或返回 false,那么推导失败。 |
(C++20 起) |
解释
占位类型说明符可以在以下语境出现:
形参声明在以下形参声明中,声明的形参的类型可以具有语法 (1):
|
(C++14 起) |
|
(C++17 起) |
(C++20 起) |
函数声明
占位类型可以在有尾随返回类型的函数声明符的声明说明符中出现。
(C++14 起) |
auto f() -> int; // OK:f 返回 int auto g() { return 0.0; } // C++14 起 OK:g 返回 double auto h(); // C++14 起 OK: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 表达式
占位类型可以在 new 表达式中的类型标识中的类型说明符序列中使用。在此类类型标识中,占位类型必须在类型说明符序列中的某个类型说明符,或者指定了要替换此类类型说明符的尾随返回类型中出现。
函数风格转型类型说明符 auto 可以作为函数风格转型的类型说明符使用。 |
(C++23 起) |
注解
C++11 之前,auto 具有存储期说明符的语义。
在以上列出的语境外使用占位类型的程序非良构。
如果一条声明声明了多个实体,并且声明说明符序列使用了占位类型,那么在满足以下任意条件时程序非良构:
- 声明的某些实体不是变量。
- 通过每个变量推导出的要替换占位类型的类型不一致。
auto f() -> int, i = 0; // 错误:以 “auto” 同时声明了函数和变量 auto a = 5, b = {1, 2}; // 错误:“auto” 指代不同类型
auto 关键词也可以用于嵌套名说明符。形如 auto:: 的嵌套名说明符是一个占位符,它会遵循受约束类型占位符的推导规则被替换为某个类或枚举类型。 |
(概念 TS) |
功能特性测试宏 | 值 | 标准 | 功能特性 |
---|---|---|---|
__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 形参声明 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}; // C++17 起错误,之前是 std::initializer_list<int> auto m{5}; // OK:DR N3922 起 m 的类型是 int,之前是 initializer_list<int> // decltype(auto) z = { 1, 2 } // 错误:{1, 2} 不是表达式 // auto 常用于无名类型,例如 lambda 表达式的类型 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 | 不能将被括号包围的表达式列表赋给 auto 变量 | 允许这种用法 |
CWG 1347 | C++11 | 一个带 auto 说明符的声明可以同时声明两个类型 分别是 T 和 std::initializer_list<T> 的变量
|
禁止这种用法 |
CWG 1852 | C++14 | decltype(auto) 中的 auto 说明符也是占位符 | 此时它不是占位符 |
CWG 1892 | C++11 | 函数指针类型标识的返回类型可以是 auto | 已禁止 |
CWG 2476 | C++11 | CWG 问题 1892的解决方案禁止从初始化器推导函数指针变量的返回类型 | 允许推导 |
引用
- C++23 标准(ISO/IEC 14882:2024):
- 9.2.9.6 Placeholder type specifiers [dcl.spec.auto]
- C++20 标准(ISO/IEC 14882:2020):
- 9.2.8.5 Placeholder type specifiers [dcl.spec.auto]
- C++17 标准(ISO/IEC 14882:2017):
- 10.1.7.4 The
auto
specifier [dcl.spec.auto]
- 10.1.7.4 The
- C++14 标准(ISO/IEC 14882:2014):
- 7.1.6.4
auto
specifier [dcl.spec.auto]
- 7.1.6.4
- C++11 标准(ISO/IEC 14882:2011):
- 7.1.6.4
auto
specifier [dcl.spec.auto]
- 7.1.6.4