基本用法与区别
auto 总是推导出“值类型”,绝不会是“引用”,如果有引用,auto会把引用去掉,推导出值类型; auto 可以附加上 const、volatile、*、& 这样的类型修饰符,得到新的类型。
auto x = 10L; // auto推导为long,x是long
auto& x1 = x; // auto推导为long,x1是long&
auto* x2 = &x; // auto推导为long,x2是long*
const auto& x3 = x; // auto推导为long,x3是const long&
auto x4 = &x3; // auto推导为const long*,x4是const long*
auto 只能用于“初始化 “, “自动类型推导”要求必须从表达式推导,那在没有表达式的时候
就需要使用decltype + 表达式,自己解决类型推导问题。
decltype 的形式很像函数,后面的圆括号里就是可用于计算类型的表达式(和 sizeof 有点 类似),其他方面就和 auto 一样了,也能加上 const、*、& 来修饰 ,它是用来直接声明变量的。
int x = 0; // 整型变量
decltype(x) x1; // 推导为int,x1是int
decltype(x)& x2 = x; // 推导为int,x2是int&,引用必须赋值
decltype(x)* x3; // 推导为int,x3是int*
decltype(&x) x4; // 推导为int*,x4是int*
decltype(&x)* x5; // 推导为int*,x5是int**
decltype(x2) x6 = x2; // 推导为int&,x6是int&,引用必须赋值
decltype 不仅能够推导出值类型,还能够推导出引用类型,也就是表达式的“原始类 型”。
delctype的缺点是 是写起来略麻烦,特别在用于初始化的时候,表达式要重复两次 (左边的类型计算,右边的初始化)
当decltype(auto)被用于声明变量时,该变量必须立即初始化。假设该变量的初始化表达式为e,那么该变量的类型将被推导为decltype(e)。也就是说在推导变量类型时,先用初始化表达式替换decltype(auto)当中的auto,然后再根据decltype的语法规则来确定变量的类型。
int x = 0; // 整型变量
decltype(auto) x2 = &x; // 推导为int*
decltype(auto) x3 = x1; // 推导为int&
常见用法
auto的 range-based for 用法
不需要关心容器元素类型、迭 代器返回值和首末位置,就能非常轻松地完成遍历操作
vector<int> v = {2,3,5,7,11}; // vector顺序容器
for(const auto& i : v) { // 常引用方式访问元素,避免拷贝代价
cout << i << ","; // 常引用不会改变元素的值
}
for(auto& i : v) { // 引用方式访问元素
i++; // 可以改变元素的值
cout << i << ",";
}
decltype 简化函数指针
// UNIX信号函数的原型,看着就让人晕
void (*signal(int signo, void (*func)(int)))(int)
// 使用decltype可以轻松得到函数指针类型
using sig_func_ptr_t = decltype(&signal) ;
decltype在类内部实现参数推导
在定义类的时候,auto 被禁用了。可以用decltype获取表达式类型,配合using生成别名
class DemoClass final {
public:
using set_type = std::set<int>; // 集合类型别名
private:
set_type m_set; // 使用别名定义成员变量
// 使用decltype计算表达式的类型,定义别名
using iter_type = decltype(m_set.begin());
iter_type m_pos; // 类型别名定义成员变量
};