占位类型说明符 (C++11 起)

来自cppreference.com
< cpp‎ | language


 
 
C++ 语言
 
 

占位类型说明符指定了后续会被替换的占位类型,替换的类型通常通过初始化器推导得到。

语法

类型约束 (可选) auto (1)
类型约束 (可选) decltype(auto) (2) (C++14 起)
类型约束 - (C++20 起) 概念名,可以有限定,可以后随以 <> 包围的模板实参列表
1)模板实参推导的规则推导类型。
2) 类型是 decltype(expr),其中 expr 是初始化器或返回语句中所用的表达式。

占位符 auto 可伴随如 const& 这样的修饰符,它们参与类型推导。占位符 decltype(auto) 必须是被声明类型的唯一组分。 (C++14 起)

如果类型约束 存在,令 T 作为该占位符的被推导类型,那么此类型约束 按如下方式引入一个约束表达式

  • 如果 类型约束Concept<A1, ..., An>,那么约束表达式为 Concept<T, A1, ..., An>
  • 否则(类型约束 是无实参列表的 Concept),约束表达式为 Concept<T>

如果约束表达式无效或返回 false,那么推导失败。

(C++20 起)

解释

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

形参声明

在以下形参声明中,声明的形参的类型可以具有语法 (1)

  • 如果 lambda 表达式的某个形参具有占位类型,那么该 lambda 表达式是泛型 lambda。
(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)

关键词

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}; // 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 说明符的声明可以同时声明两个类型
分别是 Tstd::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]
  • 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 specifier [dcl.spec.auto]