22. 类型自动推导
一、auto类型推断
C++ 11 之前,auto 与 static 是对应的,表示变量是自动存储的,但是非 static 的局部变量默认都是自动存储的,因此这个关键字变得非常鸡肋。在 C++ 11 中,为它赋予了新的含义,使用这个关键字可以自动推导出变量的类型。
C++ 11 中,auto 并不代表一种实际的数据类型,它只是一个类型声明的 “占位符”,auto 并不是万能的在任意场景上都能推导出变量的实际类型。使用 auto 声明的变量必须要进行 初始化,以便让编译器推导出它的实际类型,在编译时将 auto 占位符替换成真正的类型,它的使用语法如下:
auto 变量名 = 变量值;
auto 还可以和指针,引用结合起来,也可以带上 const、volatile 关键字,auto 在不同场景上有对应的不同的推导规则。
- 如果 auto 声明的变量是按值初始化,则推导出的类型会忽略 cv 限定符。进一步解释为,在使用 auto 声明变量时,既没有使用引用,也没有使用指针,那么编译器在推导的时候会忽略 const 和 volatile 限定符。当然 auto 本身也支持添加 cv 限定符。
- 使用 auto 声明变量初始化时,目标对象如果是引用,则引用属性会被忽略。
- 使用 auto 声明变量,如果目标对象是一个数组或者函数,则 auto 会被推导为对应的指针类型。
#include <iostream>
using namespace std;
int main(void)
{
auto x = 3.14; // double
auto y = 520; // int
auto z = 'a'; // char
// 如果 auto 声明的变量是按值初始化,则推导出的类型会忽略 cv 限定符
const int i = 5;
auto j = i; // auto: int
int temp = 110;
auto *a = &temp; // auto: int
auto b = &temp; // auto: int*
auto &c = temp; // auto: int
// 使用 auto 声明变量初始化时,目标对象如果是引用,则引用属性会被忽略
auto d = c; // auto: int
// 使用 auto 声明变量,如果目标对象是一个数组或者函数,则 auto 会被推导为对应的指针类型
int array[3];
auto e = array; // auto: int*
return 0;
}
auto 自动类型推导并不是万能的,它在以下情景无法使用:
【1】、不能作为函数的形参使用,这是因为只有在函数调用的时候才会给形参传递实参,auto 要求必须要给修饰的变量赋值,因此二者矛盾。
int func(auto a, auto b) // error
{
cout << "a: " << a << ", b: " << b << endl;
}
【2】、不能用于类的非静态成员的初始化,这是因为类的非静态成员不属于类,它是属于对象的。只有对象被创建时,类的非静态成员才能进行初始化操作(类的静态成员不允许在类的内部直接初始化)。
class Person
{
auto v1 = 0; // error
static auto v2 = 0; // error,类的静态成员不允许在类的内部直接初始化
static const auto v3 = 0; // ok
};
【3】、不能使用 auto 关键字定义数组。
int main(void)
{
int array[3];
auto a1[3]; // error
auto a1 = array; // ok,这里定义了一个指针
return 0;
}
【4】、无法使用 auto 推导模板参数。
template <typename T>
class MyClass
{
public:
T a;
};
int main(void)
{
MyClass<int> t1;
MyClass<auto> t2; // error
return 0;
}
二、decltype类型推断
在某些情况下,如果不需要或者不能定义变量,但是希望得到某种类型,这时就可以使用 C++ 11 提供的 decltype
关键字,它的作用是 在编译器编译的时候推导出一个表达式类型,语法如下:
decltype(表达式)
decltype 是 declare type 的缩写,意思是 “声明类型”。decltype 的推导是在编译期间完成的,它只是用于表达式类型的推导,并不会计算表达式的值。
#include <iostream>
using namespace std;
int main(void)
{
int a = 10;
decltype(a) b = 100; // b: int
decltype(a + 3.14) c = 3.33; // c: double
decltype(a + b * c) d = 2.512; // d: double
return 0;
}
如果我们使用 decltype 推导的表达式为 普通变量 或者 普通表达式 或者 类表达式,在这种情况下,使用 decltype 推导出的类型和表达式的类型是一致的。
如果我们使用 decltype 推导的表达式是 函数调用,则推导出的结果与函数的返回值一致。但如果函数的返回值是一个 纯右值(右值则是那些临时性的、没有持久状态的表达式,如字面量、临时对象等,它们通常无法被取地址)的话,只有类类型可以携带 const、volatile 限定符,除此之外的情况,需要忽略掉这两个限定符。
如果我们使用 decltype 推导的表达式是 左值(左值通常指的是那些有持久状态的表达式,比如变量、数组元素等,它们有明确的内存地址,可以被取地址操作符 & 获取地址。)会使用 ()
包围,如果右 const、volatile 限定符则不能忽略。
三、返回值类型后置
在 C++11 及以后的版本中,函数返回值类型可以后置(trailing return type)。这种语法在处理模板函数时特别有用,因为有时候编译器无法直接推导出模板函数的返回类型。使用 auto
关键字和 decltype
运算符可以方便地指定后置返回类型。后置返回类型的语法如下:
auto 函数名(形参列表) -> decltype(参数表达式)
返回值类型后置的基本语法是使用 auto
关键字作为占位符,然后在函数声明末尾使用 ->
后跟实际的返回类型。
template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u)
{
return t + u;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具