极客时间-《罗剑锋的 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推导类型
};

规则:

  1. auto 总是推导出“值类型”,绝不会是“引用”。
  2. 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:

  1. 变量声明和初始化时使用更好,获取值类型。
  2. range-based for 使用,为了保证效率,最好使用“const auto&”或者“auto&”。
  3. 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:

  1. 可以用在任意场合。(泛型编程中经常用到。)
  2. 当你感觉“这里我需要一个特殊类型”的时候,选它就对了。
  3. 推导类数据成员类型。

代码演示:使用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;                   // 类型别名定义成员变量
};
01-18 00:19