auto 和 decltype 的改进

auto 和 decltype 的改进
苏丙榅1. auto 的改进
auto 和 decltype 基础 –> 自动类型推导
C++14 对 auto 关键字进行了非常显著的增强,主要解决了 C++11 中 auto 使用起来比较繁琐的问题,并引入了两大核心改进:函数返回类型推导 和 泛型 lambda。
1.1 函数返回类型推导
C++ 中的 auto 类型推导是 C++11 引入的核心特性之一。它主要遵循 模板类型推导 的规则,在 C++14 中允许函数使用 auto 推导返回类型(不含尾置返回类型)。
在 C++11 中必须使用尾置返回类型:
1 | auto func() -> int { return 42; } |
在 C++14 中可以直接使用 auto 进行返回值类型推导:
1 | // 编译器自动推导为 double |
在使用auto进行函数返回值类型推导的时候,需要注意以下几点:
当变量不是指针或者引用类型时,推导的结果中不会保留
const、volatile关键字如果函数有多个返回语句,它们推导出的类型必须一致。
如果是递归调用,必须至少有一个非递归的返回语句,以便编译器确定返回类型。
1.2 lambda 参数支持 auto
C++14 允许 lambda 表达式参数使用 auto(泛型 Lambda):
在 C++11 中必须指定参数类型:
1 | auto lambda = [](int x, int y) { return x + y; }; |
在 C++14 中 lambda 表达式参数可以是 auto :
1 | auto generic_lambda = [](auto x, auto y) { |
2. decltype
在 C++14 中,关于 decltype 的改进和优化主要是为了弥补 C++11 中其在类型推导和易用性方面的不足。虽然 decltype 的核心语法规则没有改变,但 C++14 引入了新语法和上下文支持,使其更加强大和易用。
2.1 引入 decltype(auto)
这是 C++14 针对 decltype 最著名的扩展。它解决了 C++11 中 auto 推导规则(剥离引用和 const)与 decltype 推导规则(保留引用和 const)之间的割裂。
在 C++11 中,如果你想写一个返回函数调用结果的通用包装器,并保留原始的值类别(比如返回引用),你必须使用尾置返回类型:
1 | // C++11 写法:冗长 |
C++14 允许我们直接写成这样:
1 | // C++14 写法:简洁,且保留了 decltype 的推导规则 |
推导函数返回值使用auto和使用decltype(auto)的特性对比:
auto:初始化表达式如果是引用,推导为值类型(拷贝)。decltype(auto):初始化表达式如果是引用,推导为引用类型。
1 | std::string& getName(); |
2.2 decltype 对括号的敏感性
括号敏感性虽然这是 decltype 自带的规则,但在 C++14 的 decltype(auto) 语境下显得尤为重要。C++14 明确了 decltype(auto) 对变量名和表达式的不同处理方式:
如果
decltype里的参数是一个不加括号的变量名(如x),推导结果就是该变量的类型。如果
decltype里的参数是一个表达式(如x+y或vec[i]),推导结果取决于该表达式的值类别:只要
decltype的参数被括号( ... )包裹,该参数在语法上就被视为一个表达式,而不是一个变量名。既然是表达式,接下来就要看这个表达式是左值 还是 右值:- 如果是左值(L-value,如变量),推导结果为 类型的引用 (
T&)。 - 如果是右值(R-value,如临时对象或字面量),推导结果为 类型本身 (
T)。
- 如果是左值(L-value,如变量),推导结果为 类型的引用 (
为了更清楚地解释,我们看 decltype(auto) 何时会因为括号而改变结果(注意这样可能会产生返回局部变量引用的隐患):
情况1
1
2int x = 10;
decltype(auto) a = x;不加括号,等价于
decltype(x),类型是int情况2
1
2int x = 10;
decltype(auto) b = (x);加括号,等价于
decltype((x)),x是左值表达式,它有内存地址,可以赋值。类型推导为int&情况3
1
2
3
4
5int x = 10;
decltype(auto) test1()
{
return x;
}等价于
decltype(x),x是变量名,不是复杂的表达式。推导结果:int(值类型)情况4
1
2
3
4
5int x = 10;
decltype(auto) test2()
{
return (x);
}等价于
decltype((x)),加了括号,被视为一个表达式。x是左值,所以推导为左值引用。推导结果:int&(引用类型)。警告:如果 x 是局部变量,这里返回悬空引用!
1
2
3
4
5
6// 错误示范
decltype(auto) test2()
{
int x = 10;
return (x); // 警告⚠️:返回局部变量的应用
}情况5
1
2
3
4
5int x = 5, y = 9;
decltype(auto) test3()
{
return (x+y);
}等价于
decltype((x+y)),加了括号,被视为一个表达式。虽然(x+y)被视为一个表达式,但关键在于(x+y)这个表达式的 <值类别> 是 <右值>,而不是 <左值>。因为
x + y的计算结果产生了一个临时数值(比如 10),这个临时数值没有在内存中具体的地址,不能被赋值,所以(x+y)是一个右值表达式。













