C++11:自动类型推导 auto 和 decltype(下)

前面两篇文章分别介绍了 auto 和 decltype,今天聊聊它们两者之间的区别。

语法格式的区别

auto varname = value; // auto的语法格式
decltype(exp) varname [= value]; // decltype的语法格式

其中,varname 表示变量名,value 表示赋给变量的值,exp 表示一个表达式,方括号[]表示可选。

auto 要求变量必须初始化,也就是在定义变量的同时必须给它赋值;而 decltype 不要求,初始化与否都不影响变量的类型。

这很容易理解,因为 auto 根据变量初始值来推导出变量类型,如果不初始化,变量的类型也就无法推导。

推导规则不同

autodecltype 都会自动推导出变量 varname 的类型:

  • auto 根据 = 右边的初始值 value 推导出变量的类型;
  • decltype 根据 exp 表达式推导出变量的类型,跟 = 右边的 value 没有关系。

auto 将变量的类型和初始值绑定在一起,而 decltype 将变量的类型和初始值分开;虽然 auto 的书写更加简洁,但 decltype 的使用更加灵活。

应用场景不同

auto 通常用于变量声明时,简化类型定义,特别是当类型名较长或复杂时。

decltype 常用于需要精确类型匹配的场景,如模板元编程、泛型编程中,以及定义依赖于表达式类型的变量。

对 cv 限定符的处理

cv 限定符constvolatile 关键字的统称:

  • const 关键字表示数据是只读的,也就是不能被修改;
  • volatile 表示数据是可变的、易变的,目的是不让 CPU 将数据缓存到寄存器,而是从原始的内存中读取。

在推导变量类型时,auto 和 decltype 对 cv 限制符 的处理是不一样的。decltype 会保留 cv 限定符,而 auto 有可能会去掉 cv 限定符。

以下是 auto 关键字对 cv 限定符的推导规则:

  • 如果表达式的类型不是指针或者引用,auto 会把 cv 限定符直接抛弃,推导成 non-const 或者 non-volatile 类型。
  • 如果表达式的类型是指针或者引用,auto 将保留 cv 限定符。

下面的例子演示了对 const 限定符的推导:

int main() {
    // 非指针非引用类型
    const int n1 = 0;
    auto n2 = 10; // n2 是 int
    n2 = 99;
    decltype(n1) n3 = 20; // n3 是 const int
    n3 = 5;  // 错误
    // 指针类型
    const int *p1 = &n1;
    auto p2 = p1; // p2 是 const int *
    *p2 = 66;  // 错误
    decltype(p1) p3 = p1; // p3 是 const int *
    *p3 = 19;  // 错误
}

编译结果:

编译错误

在 C++ 中无法将一个变量的完整类型输出,我们通过对变量赋值来判断它是否被 const 修饰;如果被 const 修饰那么赋值失败,如果不被 const 修饰,那么赋值成功。虽然这种方案不太直观,但也是能达到目的的。

n2 赋值成功,说明不带 const,也就是 const 被 auto 抛弃了,这验证了 auto 的第一条推导规则。p2 赋值失败,说明是带 const 的,也就是 const 没有被 auto 抛弃,这验证了 auto 的第二条推导规则。

n3 和 p3 都赋值失败,说明 decltype 不会去掉表达式的 const 属性。

对引用的处理

当表达式的类型为引用时:

  • decltype 会保留引用类型;
  • auto 会抛弃引用类型,直接推导出它的原始类型。

请看下面的例子:

#include <iostream>

int main() {
    int n = 15;
    int &r1 = n;
    //auto推导
    auto r2 = r1;
    r2 = 20;
    std::cout << n << ", " << r1 << ", " << r2 << std::endl;
    //decltype推导
    decltype(r1) r3 = n;
    r3 = 115;
    std::cout << n << ", " << r1 << ", " << r3 << std::endl;
    return 0;
}

输出

15, 15, 20
115, 115, 115

从运行结果可以看出,给 r2 赋值并没有改变 n 的值,这说明 r2 没有指向 n,单独拥有了一块内存,这就证明 r2 不再是引用类型,它的引用类型被 auto 抛弃了。

给 r3 赋值,n 的值也跟着改变,说明 r3 仍然指向 n,它的引用类型被 decltype 保留了。

总结

auto 书写格式比 decltype 简单,但它的推导规则复杂,有时候会改变表达式的原始类型;而 decltype 比较纯粹,一般会坚持保留原始表达式的任何类型。

从代码的健壮性考虑,推荐使用 decltype,但是 decltype 有时显得比较麻烦,尤其是当表达式比较复杂时,例如:

std::vector<int> nums;
decltype(nums.begin()) it = nums.begin();

而如果使用 auto 就会简单很多:

std::vector<int> nums;
auto it = nums.begin();

在实际开发中,如果明确知道使用的变量类型,那么使用 auto,减少代码量,增加可读性,其他场景使用 decltype

相关推荐

  1. C++11中的类型推演工具decltype

    2024-03-17 03:34:01       9 阅读
  2. c++11 auto类型推导的使用建议注意事项

    2024-03-17 03:34:01       13 阅读
  3. C++中autodecltype的用法

    2024-03-17 03:34:01       33 阅读
  4. 110 C++ decltype含义,decltype 主要用途

    2024-03-17 03:34:01       24 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-17 03:34:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-17 03:34:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-17 03:34:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-17 03:34:01       18 阅读

热门阅读

  1. 程序员如何选择职业赛道

    2024-03-17 03:34:01       17 阅读
  2. LeetCode -- 76. 最小覆盖子串

    2024-03-17 03:34:01       19 阅读
  3. 前端如何识别上传的二维码---jsQR

    2024-03-17 03:34:01       19 阅读
  4. 计算机安全

    2024-03-17 03:34:01       19 阅读
  5. MySQL 中的锁机制详解

    2024-03-17 03:34:01       20 阅读
  6. transformer注意力权重系数绘图

    2024-03-17 03:34:01       20 阅读
  7. vue数据

    vue数据

    2024-03-17 03:34:01      15 阅读
  8. 以太坊的扩容方案之二层网络 L2-Rollup & zkEVM

    2024-03-17 03:34:01       18 阅读
  9. linux让前台正在执行的命令转入后台并nohup的方法

    2024-03-17 03:34:01       20 阅读
  10. 动态规划 Leetcode 96 不同的二叉搜索树

    2024-03-17 03:34:01       21 阅读
  11. CSV Excel乱码问题 和 BOM标记

    2024-03-17 03:34:01       20 阅读
  12. SpringBoot之yml与properties配置文件格式的区别

    2024-03-17 03:34:01       19 阅读
  13. gazebo_ros和ros_ign_gazebo

    2024-03-17 03:34:01       17 阅读
  14. python calendar内置日历库函数方法

    2024-03-17 03:34:01       17 阅读