极客时间-《罗剑锋的 C++ 实战笔记》文章笔记 + 个人思考
语言特性
06 | auto/decltype:为什么要有自动类型推导?
auto 在C++ 11 引入。
为什么说C++是静态强类型语言?
编译阶段对数据类型进行严格检查,编译阶段每个变量和表达式的类型都是确定的。
通过自动类型推导 auto 在编译阶段从编译器获取类型。
auto可以自适应表达式类型。(例如使用C++关联容器,把 map 改为 unordered_map,使用 auto 的代码不需要修改。)
auto 的自动推导只能用在初始化。(赋值初始化或列表初始化。)
代码演示:变量右边必须要有一个表达式。
auto x = 0L; // 自动推导为long
auto y = &x; // 自动推导为long*
auto z {&x}; // 自动推导为long*
auto err; // 错误,没有赋值表达式,不知道是什么类型
代码演示:类成员变量初始化不允许使用 auto
class X final
{
auto a = 10; // 错误,类里不能使用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*
decltype 通过在 () 中 输入可用于计算类型的表达式,编译器在编译阶段获取表达式类型。
相对于 auto 只能用于初始化,decltype 应用场景更多。
代码演示:decltype 使用
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 能够推推导出引用。
decltype 能够推推导出引用,其他方面就和 auto 一样了,也能加上 const、*、& 来修饰。
decltype 可以用在变量声明、函数参数 / 返回值、模板参数等任何类型能出现的地方。
decltype 写起来稍微麻烦一点:decltype(x)& x2 = x;
表达式写了2遍。
C++14 新增 decltype(auto)
代码演示:decltype(auto)
int x = 0; // 整型变量
decltype(auto) x1 = (x); // 推导为int&,因为(expr)是引用类型。 x是值类型,加上括号就变成了引用类型。
decltype(auto) x2 = &x; // 推导为int*
decltype(auto) x3 = x1; // 推导为int&
auto:
- 变量声明和初始化时使用更好,获取值类型。
- range-based for 使用,为了保证效率,最好使用“const auto&”或者“auto&”。
- C++14,推导函数返回值类型。
代码演示:range-based for 使用,为了保证效率,最好使用“const auto&”或者“auto&”。
vector<int> v = {2,3,5,7,11}; // vector顺序容器
for(const auto& i : v) { // 常引用方式访问元素,避免拷贝代价
cout << i << ","; // 常引用不会改变元素的值
}
for(auto& i : v) { // 引用方式访问元素
i++; // 可以改变元素的值
cout << i << ",";
}
代码演示:C++14,推导函数返回值类型。
auto get_a_set() // auto作为函数返回值的占位符
{
std::set<int> s = {1,2,3};
return s;
}
decltype:
- 可以用在任意场合。(泛型编程中经常用到。)
- 当你感觉“这里我需要一个特殊类型”的时候,选它就对了。
- 推导类数据成员类型。
代码演示:使用decltype可以轻松得到函数指针类型
// UNIX信号函数的原型,看着就让人晕,你能手写出函数指针吗?
void (*signal(int signo, void (*func)(int)))(int)
// 使用decltype可以轻松得到函数指针类型
using sig_func_ptr_t = decltype(&signal) ;
代码演示:推导类数据成员类型。
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; // 类型别名定义成员变量
};