一、auto
关键字
作用
auto
允许编译器根据变量的初始化表达式自动推导其类型,无需显式指定。
C++11 的改进
基本用法
auto x = 5; // x 推导为 int auto y = 3.14; // y 推导为 double auto str = "hello"; // str 推导为 const char*
推导规则
auto
会忽略顶层const
和引用,但保留底层const
:const int a = 10; auto b = a; // b 类型为 int(忽略顶层 const) const auto c = a; // c 类型为 const int int& ref = x; auto d = ref; // d 类型为 int(忽略引用) auto& e = ref; // e 类型为 int&(显式保留引用)
常见应用场景
- 简化迭代器声明:
std::vector<int> vec{1, 2, 3}; for (auto it = vec.begin(); it != vec.end(); ++it) { /* ... */ }
- 范围
for
循环:for (auto& elem : vec) { elem *= 2; } // 修改容器元素
- 泛型编程:
template <typename T, typename U> auto add(T t, U u) -> decltype(t + u) { return t + u; }
- 简化迭代器声明:
注意事项
auto
变量必须初始化:auto value; // 错误:无法推导类型
- 避免推导为不期望的类型(如
std::initializer_list
):auto list = {1, 2, 3}; // list 类型为 std::initializer_list<int> //该问题在C++17中得到了修复
二、decltype
关键字
作用
decltype(expr)
返回表达式 expr
的类型,保留顶层 const
和引用,且不计算 expr
的值。
将变量的类型声明为表达式指定的类型。
C++98 的局限性
- 无法直接获取表达式的类型,需依赖类型萃取(如
typeid
,但丢失类型信息)。
C++11 的改进
基本用法
int x = 5; const int& rx = x; decltype(x) a = x; // a 类型为 int decltype(rx) b = x; // b 类型为 const int& decltype(5 + 3.14) c; // c 类型为 double
推导规则
- 若
expr
是变量:返回变量声明类型(包括const
和引用)。 - 若
expr
是表达式:返回表达式的结果类型(保留值类别和const
):int i = 0; int* p = &i; decltype(*p) d = i; // d 类型为 int&(解引用操作返回左值) decltype(i + 0) e; // e 类型为 int(表达式结果为纯右值)
- 若
常见应用场景
- 函数返回类型后置(结合
auto
):template <typename T, typename U> auto add(T t, U u) -> decltype(t + u) { return t + u; }
- 模板元编程:
template <typename T> auto clone(const T& obj) -> decltype(new T(obj)) { return new T(obj); }
- 保留引用和
const
:const std::vector<int> vec{1, 2, 3}; decltype(vec)::value_type x = vec[0]; // x 类型为 const int
- 函数返回类型后置(结合
与
auto
的对比特性 auto
decltype
推导依据 初始化表达式 任意表达式或变量 是否计算表达式 需要初始化(计算) 不计算表达式 const
和引用处理忽略顶层 const
和引用保留所有修饰符
在C++中,->
符号在函数声明中用于 后置返回类型语法,它允许你将函数的返回类型写在参数列表之后。这种写法主要用于以下场景:
当函数的返回类型依赖于参数时,传统的返回类型前置写法无法直接表达,例如:
// 传统写法(错误示例):返回类型需要用到参数 t 和 u,但此时它们尚未声明!
decltype(t + u) add(T t, U u) { ... } // 编译报错
通过后置语法,可以先用 auto
占位返回类型,再用 ->
指定实际类型:
// 正确写法:返回类型后置
auto add(T t, U u) -> decltype(t + u) { ... }
三、声明相关改进
1. 类型别名模板 (using
)
- C++98:仅支持
typedef
,无法直接定义模板别名。typedef std::map<std::string, int> StringIntMap; // 普通类型别名
- C++11:支持
using
定义模板别名,更清晰且支持模板参数:template <typename T> using Vec = std::vector<T>; // 模板别名 Vec<int> vec{1, 2, 3};
2. 范围 for
循环
std::vector<int> vec{1, 2, 3};
for (auto& val : vec) { val *= 2; }
3. 委托构造函数
- C++98:构造函数无法直接调用其他构造函数。
- C++11:允许构造函数委托:
class Widget { public: Widget(int x) : x_(x) {} Widget() : Widget(0) {} // 委托给 Widget(int) private: int x_; };
四、对比总结
特性 | C++98 | C++11 |
---|---|---|
变量类型声明 | 必须显式指定 | 支持 auto 自动推导 |
类型推导 | 无直接支持 | decltype 可推导任意表达式类型 |
模板别名 | typedef 不支持模板参数 |
using 支持模板别名 |
容器遍历 | 迭代器显式遍历 | 范围 for 循环直接访问元素 |
构造函数复用 | 无法委托 | 支持构造函数委托 |
五、注意事项
auto
推导陷阱:auto x = {1}; // x 类型为 std::initializer_list<int> auto y{1}; // C++11 中推导为 std::initializer_list<int> // C++17 修正为 int
decltype
与变量名:
decltype(var)
返回变量声明类型,decltype((var))
可能返回引用(若var
是左值)。
decltype(auto)
(C++14):
结合auto
和decltype
规则,保留所有修饰符:int x = 0; decltype(auto) a = x; // a 类型为 int decltype(auto) b = (x); // b 类型为 int&