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

占位类型说明符指定一个 占位类型 ,该类型稍后将被替换,通常通过从 初始化器 推导得出。

目录

语法

类型约束  (可选) auto (1)
类型约束  (可选) decltype(auto) (2) (自 C++14 起)
类型约束 - (自 C++20 起) 一个 概念 名称,可选择性地限定,可选择性地后跟用 <> 括起来的模板实参列表
1) 类型推导使用 模板实参推导 的规则进行。
2) 类型为 decltype(expr) ,其中 expr 是初始化器或用于 return 语句 的操作数。

占位符 auto 可以伴随修饰符,例如 const & ,这些修饰符将参与类型推导。 占位符 decltype ( auto ) 必须是声明类型的唯一组成部分。 (C++14 起)

若存在 类型约束 ,令 T 为占位符推导出的类型,该 类型约束 按以下方式引入 约束表达式

  • 类型约束 Concept<A 1 , ..., A n > ,则约束表达式为 Concept<T, A 1 , ..., A n >
  • 否则( 类型约束 为不带实参列表的 Concept ),约束表达式为 Concept<T>

若约束表达式无效或返回 false ,则推导失败。

(since C++20)

说明

占位符类型说明符可能出现在以下语境中:

参数声明

在下列参数声明中,被声明参数的类型可采用语法 (1)

  • lambda表达式 的参数具有占位符类型,则该lambda表达式为泛型lambda。
(C++14 起)
  • 常量模板参数 具有占位符类型,其类型将从对应的模板实参推导得出。
(C++17 起)
(C++20 起)

函数声明

占位符类型可以出现在包含尾随返回类型的 函数声明符 声明说明符 中。

占位符类型可以出现在 函数声明符 的声明说明符或 类型说明符 中声明的返回类型处。这种情况下将应用 返回类型推导

(since C++14)
auto f() -> int; // 正确:f 返回 int 类型
auto g() { return 0.0; } // C++14 起正确:g 返回 double 类型
auto h(); // C++14 起正确:h 的返回类型将在其定义时推导

变量声明

使用占位类型声明的变量类型由其 初始化器 推导得出。这种用法在变量的初始化声明中是被允许的。

占位符类型仅能作为声明说明符序列中的 声明说明符 之一出现,或作为尾随返回类型中的类型说明符之一出现,该尾随返回类型指定了替换此类声明说明符的类型。在这种情况下,声明必须至少声明一个变量,且每个变量必须具有非空的初始化器。

// 声明说明符中的 “auto”
auto x = 5; // 正确:x 的类型为 int
const auto *v = &x, u = 6; // 正确:v 的类型为 const int*,u 的类型为 const int
static auto y = 0.0; // 正确:y 的类型为 double
auto f() -> int;
auto (*fp)() -> auto = f; // 正确:尾随返回类型中的 “auto”
                          // 可从 f 推导得出

结构化绑定声明

auto 说明符可用于 结构化绑定 声明。

(since C++17)

new 表达式

占位符类型可用于 new 表达式 的类型标识中的类型说明符序列。在此类类型标识中,占位符类型必须作为类型说明符序列中的一个类型说明符出现,或者作为指定替换该类型说明符的类型的尾随返回类型出现。

函数式转型

auto 类型说明符可用作 函数式转型 的类型说明符。

(since 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;// 正确
};

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 << "通过 c2 修改前, a = " << a << '\n';
    ++c2;
    std::cout << "通过 c2 修改后, a = " << a << '\n';
    auto [v, w] = f<0>(); // 结构化绑定声明
    auto d = {1, 2}; // 正确:d 的类型是 std::initializer_list<int>
    auto n = {5};    // 正确:n 的类型是 std::initializer_list<int>
//  auto e{1, 2};    // 错误:根据 DR n3922,之前是 std::initializer_list<int>
    auto m{5};       // 正确:根据 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 的解决方案禁止了
从初始化器推导函数指针变量的返回类型
已允许
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 specifier [dcl.spec.auto]
  • C++11 标准 (ISO/IEC 14882:2011):
  • 7.1.6.4 auto 说明符 [dcl.spec.auto]