专栏首页linjinhe的专栏现代 C++:自动类型推导

现代 C++:自动类型推导

自动类型推导

现代的编程语言,不管是动态语言(JavaScript、Python 等),还是静态语言(Go、Rust 等),大都支持自动类型推导(type deduction)。

自动类型推导,通俗地讲就是定义一个变量的时候不需要明确指定类型,而是让编译器根据上下文进行推导。

在 C++11 之前,模板(template)代码就支持编译器自动类型推导。C++11 很重要的一个特性就是加强了编译器自动类型推导的能力,使之不限于模板 —— 与此相关的关键字有两个 autodecltype

auto

我们来看看 auto 关键字在 C++ 中的使用。 最简单的用法,定义变量的时候不指定类型,通过初始化的值让编译器自动推导。

auto a;                                          // 编译不通过

auto b = 0;                                    // b 是 int 类型
auto c = 0ull;                                // c 是 unsigned long long 类型  
auto d = "Hello World";           // d 是 const char* 类型
auto e = std::string("Hello"); // e 是 std::string 类型

auto 和容器类型、迭代器一起配合使用,可以少打很多字,代码也更简洁、清晰。

  std::vector<int> v(10, 1); 
  auto itr_begin = v.begin();  // std::vector<int>::iterator
  auto itr_end = v.end();         // std::vector<int>::iterator
  auto sz = v.size();                    // std::vector<int>::size_type

如果不用自动类型推导,下面 v 的类型写起来也很麻烦。如果 b 和 e 是自定义的迭代器,不一定能用 typename std::iterator_traits<Iter>::value_type 来获得类型。

template<typename Iter>
void Process(Iter b, Iter e) {
  while (b != e) {
    auto v = *b;     // 如果不用自动类型推导,如何获得 *b 的类型
    // typename std::iterator_traits<Iter>::value_type v = *b; 
    std::cout << v << std::endl;
    ++b;
  }
}

类型推导可以和 Lambda 表达式一起愉快地使用。

auto Plus = [](int a, int b) { return a + b; };

也许有人会说,Lambda 表达式可以用一个 std::function<T> 对象来包装。

std::function<int(int, int)> PlusFunc = [](int a, int b) { return a + b; };

但是这样做有几点不好:

  1. std::function<T> 内部会涉及动态内存分配,性能上劣于自动类型推导的实现;
  2. 让代码看起来复杂不少;
  3. 对于泛型 Lambda 表达式,std::function<T> 也无能为力了。
auto Plus = [](auto a, auto b) { return a + b; };    // std::function<T> 的类型没法写了
std::cout << Plus(3, 4) << std::endl;
std::cout << Plus(3.14, 1.11) << std::endl;
std::cout << Plus(std::string("hello"), std::string("world")) << std::endl;

某些情况下,自动类型推导还可以让你避免一些“坑”。比如:

std::unordered_map<std::string, int> m;
// ...
for (const std::pair<std::string, int>& pa : m) {    // 你觉得有没有问题?
    // ... 
}

看得出上面这段代码有什么问题吗?<br />上面的代码会导致复制整个 unordered_map。因为 std::unordered_map<Key, T>::value_type 的类型是 std::pair<const Key, T>。正确的写法应该是:

for (const std::pair<const std::string, Foo>& pa : m) {
    // ...
}

用自动类型推导可以简单避免这个坑:

for (const auto& pa : m) {
    // ...
}

当然,用自动类型推导的时候,也可能引入一些坑。比如:

std::vector<bool> v2; 
v2.push_back(true);
v2.push_back(false);
auto b2 = v2[0];            // b2 是什么类型?

因为 std::vector<bool> 的特殊实现原因,变量 b2 不是一个 bool 类型,而是一个自定义的类。(无论你是否使用自动类型推导,都尽可能不要使用 std::vector<bool>。)

decltype

decltype 的作用是,告诉你一个表达式/变量/常量是什么类型。比如:

std::cout << typeid(decltype(1)).name() << std::endl;   // 输出 i,表示 int

float f;
std::cout << typeid(decltype(f)).name() << std::endl;  // 输出 f,表示 float

unsigned a = 1;
unsigned long long b = 2;
std::cout << typeid(decltype(a + b)).name() << std::endl;  // 输出 y,表示 unsigned long long

typeid(T).name() 在不同的编译器下的输出可能不一样。本文在 Ubuntu 上使用 gcc 7.5 进行编译。typeid(T).name() 的输出可以通过 c++filt 工具转换成实际可读的类型名称。

相比 auto,decltype 用得少很多。 举一个例子:

template<typename T, typename U>
??? Plus(T t, U u) 
  return t + u;
}

t + u 到底应该返回什么类型?

Plus(1, 2);      // 返回值类型应该是 int
Plus(1, 2.0);  // 返回值类型应该是 double 

使用 decltype 的 trailing return type 来解决这个问题:

template<typename T, typename U>
auto Plus(T t, U u) -> decltype(t + u) {
  return t + u;
}

C++ 14 进行了加强,可以省掉这条尾巴。

template<typename T, typename U>
auto Plus(T t, U u) {
  return t + u;
}

如果函数有多个 return 语句,需要保证它们返回的类型都是一样的才能成功编译。

// error: inconsistent deduction for auto return type: ‘int’ and then ‘double’
auto f(int i) {
  if (i == 1) {
    return 1;
  } else {
    return 2.0;
  }
}

decltype(auto)

使用 auto 需要自己手动说明是值类型还是引用类型。C++14 引入 decltype(auto) 来自动推导精确类型——其实 decltype(auto) 算是 decltype(expr) 的一个语法糖。

std::vector<std::string> v{"C++98", "C++03", "C++11",
                                                    "C++14", "C++17", "C++20"};

// v[0] 的返回值类型是 std::string&,但是 a 是 std::string
auto a = v[0]; 
// a 是 std::string&
auto& b = v[0];  
// C++11,我们可以这样确定精确类型,c 是 std::string&
// 但是,如果 v[0] 变成一个复杂的表达式,代码写出来可能很难看懂
decltype(v[0]) c = v[0];  
// C++14 引入了 decltype(auto),可以自动推导出精确类型。d 是 std::string&
decltype(auto) d = v[0];

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++の自动类型推导和其他

    本来这篇文章之前已经发过了技术◈C++核心知识总结(I),但是鉴于后来更新中断了,为了后面文章能连续更新,还是再发一遍吧。这篇文章主要讲以下三个话题:

    leoay
  • c++11类型推导

    1 ParamType是一个指针或者引用(非通用universal reference引用)

    江上摆渡翁
  • TypeScript接口参数/响应类型自动推导

    TypeScript Web 项目的API 的参数与响应数据类型,如果不手动映射,默认是缺失的:

    玖柒的小窝
  • C# 自定义可迭代类型

    雪飞鸿
  • C语言(自动获取数据类型)

    有两种方法给这个关键字传参,一种是传递表达式,一种是传递数据类型。下面是获取一个表达式的类型的例子:

    用户2617681
  • Effective Modern C++翻译(3)-条款2:明白auto类型推导

    条款2 明白auto类型推导 如果你已经读完了条款1中有关模板类型推导的内容,那么你几乎已经知道了所有关于auto类型推导的事情,因为除了一个古怪的例外,aut...

    magicsoar
  • Effective Modern C++翻译(2)-条款1:明白模板类型推导

    第一章 类型推导 C++98有一套单一的类型推导的规则:用来推导函数模板,C++11轻微的修改了这些规则并且增加了两个,一个用于auto,一个用于decltyp...

    magicsoar
  • python 数据类型及推导式、迭代器和生成器

    使用namedtuple相当于创建了一个类,s1相当于实例话了一个包含name,age,gender三个属性的类。

    测开菜鸟_python
  • 在OneFlow实现数据类型自动提升

    可以观察到同样是multiply运算,有些结果的数据类型被提升到更高的一级,有些并没有被提升,还维持着int8类型。这其实是一种类型提升系统,系统内会自定义一些...

    BBuf
  • c++ - 如何自动将强类型枚举转换为int?

    a::LOCAL_A是强类型枚举试图实现的功能,但是有一个小的区别:普通枚举可以转换为整数类型,而强类型枚举不能在没有强制转换的情况下实现。

    ccf19881030
  • Effective Modern C++翻译(5)-条款4:了解如何观察推导出的类型

    条款4:了解如何观察推导出的类型 那些想要知道编译器推导出的类型的人通常分为两种,第一种是实用主义者,他们的动力通常来自于软件产生的问题(例如他们还在调试解决中...

    magicsoar
  • C# 中 string类型不足位数自动补0的方法

    在 C# 中可以对字符串使用 PadLeft 和 PadRight 进行轻松地补位。

    静谧的小码农
  • C#实现软件开机自启动原理与代码

    1、软件自启动原理     软件自启动的原理要从Windows的注册表聊起,在Windows操作系统下,主要有2个文件夹和8个注册表键项控制程序的自启动,这部分...

    waylon
  • 在Lua中实现对UE4 C++代码的自动补全

    本文介绍了在Emmylua插件的支持下,如何获取到UE4的反射信息,并如何生成Emmylua格式的Lua注释代码来支持自动补全和跳转。

    阿苏勒
  • ​测试开发进阶-2.数据类型及推导式、迭代器和生成器

    https://pythonguidecn.readthedocs.io/zh/latest/writing/structure.html

    ITester软件测试小栈
  • Effective Modern C++翻译(7)-条款6:当auto推导出意外的类型时,使用显式的类型初始化语义

    条款6:当auto推导出意外的类型时,使用显式的类型初始化语义 条款5解释了使用auto来声明变量比使用精确的类型声明多了了很多的技术优势,但有的时候,当你想要...

    magicsoar
  • 生成代码,从 T 到 T1, T2, Tn —— 自动生成多个类型的泛型

    发布于 2018-01-31 05:38 更新于 2018-05...

    walterlv
  • SAS-一个小程序实现变量类型的自动转化~

    做为标题党的小编,一贯喜欢将标题写很大...嗯,最近写了一个小程序,虽然是一个没有任何技术含量的程序,不过还是想分享给大家。这个程序实现的功能是将SAS数据集中...

    Setup
  • 推荐一款 10 行 Python 代码实现网页自动化工具

    各种各样的网站在我们日常工作和学习中占据着举足轻重的地位,学习、影音娱乐、查询资料、协同办公,越来越多的任务都被迁移到浏览器

    AirPython

扫码关注云+社区

领取腾讯云代金券