47 / 80 · C++14 · 约 9 分钟
auto 与 decltype(auto) 的推导边界
auto 让编译器推导静态类型,并不引入动态类型。是否保留引用与 const 取决于声明形式;decltype(auto) 则直接采用 decltype 规则,括号也能改变结果,尤其影响函数返回值与生命周期。
先决定值还是借用
C++11 的 auto 借助类似模板实参推导的规则确定类型。auto x = source 通常得到一个新值:忽略源对象的顶层 const 与引用;auto& x = source 则声明引用,保留被引用对象的 const。const auto& 适合只读借用,但仍需要检查源对象的生命周期。
这不是“自动选择最省拷贝的类型”。源表达式返回大对象引用时,裸 auto 仍可能复制;源类型本身是代理对象时,裸 auto 也可能复制代理而继续引用底层存储。读声明时应先判断所有权意图,再看推导出的具体类型。
decltype 的括号具有语义
对未加括号的变量名,decltype(name) 返回该实体的声明类型;对一般表达式则根据值类别决定:左值得到 T&,将亡值得到 T&&,纯右值得到 T。所以普通整数 x 的 decltype(x) 是 int,而 decltype((x)) 是 int&。
C++14 的 decltype(auto) 将初始化表达式或返回表达式直接交给这套规则。它不能像 auto 那样再随意拼接 & 修饰。示例中的两个访问函数只有一处括号不同,却分别返回整数副本与原整数的引用,这种差异应在接口设计中有明确理由。
返回推导不能替你检查生命期
包装已有函数、必须准确保留其引用返回值时,decltype(auto) 很有用;只想返回独立结果时,auto 或明确的值类型通常更易审查。返回局部变量的括号表达式可能推出局部引用,函数退出后立即悬空,编译通过不代表安全。
示例让引用只绑定 main 中仍存活的 n,并用类型断言说明每次推导。另一个容易误判的地方是花括号:auto a = {1, 2} 推导 initializer_list,而不是 vector;现代规则下 auto b{1} 是 int。选择括号形式前,应先确定自己要的是单个值还是列表。
容易答错的地方
- decltype(auto) 的 return (local) 会保留局部变量引用;不能通过给 return 随手加括号来“统一风格”。
- 裸 auto 不保证脱离源存储,例如 vector<bool> 的元素访问可能返回代理;要独立 bool 值就显式转换为 bool。
运行一个例子
最低标准 C++14 · 完整程序 · 下载 .cpp
#include <cassert>
#include <iostream>
#include <type_traits>
struct Box { int value; };
decltype(auto) copy_value(Box& b) { return b.value; }
decltype(auto) borrow_value(Box& b) { return (b.value); }
int main() {
const int source = 3;
auto copy = source;
auto& ref = source;
static_assert(std::is_same<decltype(copy), int>::value, "value");
static_assert(std::is_same<decltype(ref), const int&>::value, "borrow");
Box b{4};
static_assert(std::is_same<decltype(copy_value(b)), int>::value, "copy return");
static_assert(std::is_same<decltype(borrow_value(b)), int&>::value, "reference return");
auto detached = copy_value(b);
borrow_value(b) = 9;
assert(detached == 4 && b.value == 9);
std::cout << detached << ' ' << b.value << '\n';
}
在本地编译
g++ -std=c++14 -Wall -Wextra -Wpedantic -pthread modern-auto.cpp -o example && ./example预期结果
4 9
CHECK YOUR UNDERSTANDING
合上答案,试着解释。
若把 auto detached = copy_value(b) 改成 decltype(auto) detached = borrow_value(b),最后 detached 是多少?如何保留数值副本?
查看参考答案
detached 成为 int&,给 borrow_value(b) 赋值后它也观察到 9。写 auto detached = borrow_value(b) 或 int detached = borrow_value(b) 可复制当时的数值 4。区别在调用者声明,不在函数是否具有“自动推导”字样。
继续查证
标准草案链接会随工作草案更新;本文版本标记对应示例最低要求,不表示草案中的所有新规则都适用于旧标准。