现代 C++ 编程实战
吴咏炜
前 Intel 资深软件架构师
34196 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
加餐 (1讲)
现代 C++ 编程实战
15
15
1.0x
00:00/00:00
登录|注册

08 | 易用性改进 I:自动类型推断和初始化

获取表达式的引用类型
获取变量的精确类型
类型推导规则
用法示例
用法
用途
规则
用法
用法
避免语法分析问题
用法
技术细节
用法
用法
自动推导机制
array的使用
make_pair的使用
后置返回值类型声明
使用auto或decltype(auto)声明返回值类型
decltype(auto)
decltype
auto
类数据成员的默认初始化
统一初始化
列表初始化
结构化绑定
类模板的模板参数推导
函数返回值类型推断
自动类型推断
易用性改进 I:自动类型推断和初始化

该思维导图由 AI 生成,仅供参考

你好,我是吴咏炜。
在之前的几讲里,我们已经多多少少接触到了一些 C++11 以来增加的新特性。下面的两讲,我会重点讲一下现代 C++(C++11/14/17)带来的易用性改进。
就像我们 [开篇词] 中说的,我们主要是介绍 C++ 里好用的特性,而非让你死记规则。因此,这里讲到的内容,有时是一种简化的说法。对于日常使用,本讲介绍的应该能满足大部分的需求。对于复杂用法和边角情况,你可能还是需要查阅参考资料里的明细规则。

自动类型推断

如果要挑选 C++11 带来的最重大改变的话,自动类型推断肯定排名前三。如果只看易用性或表达能力的改进的话,那它就是“舍我其谁”的第一了。

auto

自动类型推断,顾名思义,就是编译器能够根据表达式的类型,自动决定变量的类型(从 C++14 开始,还有函数的返回类型),不再需要程序员手工声明([1])。但需要说明的是,auto 并没有改变 C++ 是静态类型语言这一事实——使用 auto 的变量(或函数返回值)的类型仍然是编译时就确定了,只不过编译器能自动帮你填充而已。
自动类型推断使得像下面这样累赘的表达式成为历史:
// vector<int> v;
for (vector<int>::iterator
it = v.begin(),
end = v.end();
it != end; ++it) {
// 循环体
}
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

C++11/14/17引入了许多易用性改进,其中最重要的是自动类型推断。通过`auto`关键字,编译器能够根据表达式的类型自动决定变量的类型,大大简化了代码。此外,`decltype`用于获取表达式的类型,`decltype(auto)`语法在通用的转发函数模板中非常方便。C++14还引入了函数返回值类型推断,可以使用`auto`或`decltype(auto)`来声明函数的返回值类型。另外,C++17还引入了类模板的模板参数推导和结构化绑定,使得代码更加简洁和易读。这些改进提高了代码的抽象性,降低了代码的啰嗦程度,使得开发者能够更轻松地编写通用的功能。 在C++98中,标准容器初始化相对繁琐,而C++11引入了列表初始化,允许以更简单的方式来初始化对象,这不仅适用于标准库容器,还适用于各种类的方法。另外,统一初始化语法使用大括号 `{}` 取代小括号 `()`,避免了C++中“最令人恼火的语法分析”,并且在某些情况下可以省略类名进行构造。类数据成员的默认初始化允许在声明数据成员时直接给予一个初始化表达式,简化了构造函数的初始化过程。 这些现代C++特性的引入大大简化了代码编写,提高了代码的可读性和可维护性。读者可以通过使用自动类型推断、列表初始化、统一初始化和类数据成员的默认初始化等特性,使代码更加简洁高效,提高开发效率。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《现代 C++ 编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(33)

  • 最新
  • 精选
  • robonix
    老师,文中提到auto&& a = expr; 推断结果是一个跟 expr 值类别相同的引用类型。那么如果expr是左值引用或右值引用,对应的推断类型又是啥呢?

    作者回复: 仍然是第3讲里的规则,左值得到左值引用,右值得到右值引用(但要注意右值引用是个左值): int x = 42; int& a = x; int&& b = 42; auto&& c = a; // int& auto&& d = b; // int& auto&& e = std::move(b); // int&&

    2020-01-06
    2
    18
  • 中年男子
    建议各位如果文章中有没看懂的地方,去看看老师在文末的参考资料,这些也都是好东西

    作者回复: 识货。😇 毕竟这个专栏的篇幅是 30 讲,不是 60 讲或 100 讲啊。

    2019-12-14
    12
  • 花晨少年
    如果一个类有使用初始化列表的构造函数时,则只应用在初始化列表构造的情况。 是说{1.0}这种形式只用在初始化列表构造的情况吗? 什么是初始化列表构造的情况?不明白

    作者回复: 是说如果一个类Obj既有: Obj(initializer_list<int>); 又有: Obj(double); 那你想调用后面那个构造函数,就别用 Obj{1.0} 这种形式,而用 Obj(1.0)。

    2019-12-14
    7
  • Geek_68d3d2
    在使用模板的时候为什么T是容器的时候前面要加上一个typename?? 比如typename T::const_iterator it = v.begin()

    作者回复: 语言规则要求。在编译器不知道T是啥(模板定义中,尚未实例化)的情况下,显式通知编译器T::const_iterator是个类型。

    2020-06-11
    6
  • 晚风·和煦
    转发引用就是万能引用吗😁😂

    作者回复: 对。C++11刚出来时,对T&&没有专门的术语,是Scott Meyers发明了“万能引用”这一术语来描述。后来标准委员会认识到我们需要一个专门术语,在C++17把这个概念用“转发引用”来描述了。为什么叫这个名字,可以参见: https://isocpp.org/files/papers/N4164.pdf

    2020-02-16
    4
  • 阿白
    老师我在练习结构化绑定的时候发现一个问题,下面这个例子 int p = 1; char l = 1; int m = 1; std::tuple<float &, char &&, int> tpl(p, std::move(l), m); const auto&[a, b, c] = tpl; 最终a,b,c类型的推断结果为float & a, char && b, const int& c 引用类型的cosnt限定被忽略了,我去查资料查到 Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef or of a template type argument, in which case the cv-qualifiers are ignored. 我在测试的时候感觉很混乱,到底什么情况下会出现ill-formed的Cv-qualified references。

    作者回复: 你举的这个例子相当复杂。常见情况下,一般不会用引用绑定去绑定到一个带引用的 tuple 上,所以我也不讨论这样的特殊情况了。 对于这个例子,编译器的第一步动作是: const auto& e = tpl; 我们随即得到 e 的类型是 const std::tuple<float &, char &&, int>&。 a、b、c 本质上就是 get<0>(e)、get<1>(e)、get<2>(e) 的语法糖了。对于 tuple 里的引用成员,get 的结果就是这个引用成员,所以没有 const:你对它修改时没有改变 tuple 本身,而是改变它指向的内容(可以把引用改成指针来想象一下;引用可以看作会自动解引用的指针)。对于 tuple 里的非引用成员,get 的结果实际是指向 tuple 的引用(即使你获得的结果不是引用)。这就好比 s.int_value 的类型是整数,但你修改了 s.int_value 会修改 s 一样。 下面的例子可以帮助你理解: int n = 0; std::tuple<int*, int> tup{&n, n}; auto& [p, m] = tup; m = 1; 到此为止,tup 里的整数也变成了 1。

    2021-10-29
    3
    1
  • 怪兽
    我孤陋寡闻了,C++的语法分析竟然会在隐式函数转换上失败,我试了一下,如果这样写就可以: ifstream ifs(utf8_to_wstring(filename).operator wchar_t*()); 另外,我在使用map容器的时候,总是first、second,我觉得应用结构化绑定更直观些,那么是否依旧可以使用&(引用)符号呢?例如auto&,这样就不会发生拷贝了吧: std::map<int, string> students; for (const auto& [id, name] : students) { cout << id << name << endl; }

    作者回复: 对,对于遍历关联容器,你写的是标准的惯用法。

    2021-06-19
    1
  • xl000
    老师,decltype(auto) a = expr;这种写法,根auto a = expr;有什么区别呢?它能正确地推断出需要写auto& a = expr;的情况?

    作者回复: 区别不是写在正文里了么? 对,在某些情况下它就相当于 auto& a = exp;。

    2020-04-02
    2
    1
  • Geek_b68b74
    什么时候该用auto,什么时候该用decltype呢?隐隐知道怎么用,但不知道具体的规则是什么呢

    作者回复: 就看你要不要保持值类别了。在应用的代码,一般写 auto 更安全。如果你是写通用的模板代码,那可能就需要写 decltype(auto) 了。但你得仔细考虑一下对象的生命期问题,确保不会返回一个过期的引用。

    2020-01-17
    1
  • EncodedStar
    auto 用了不少,真的好用,其他的都没有真正用到。工作中的项目都不支持11,只能自己学习的时候使用了。

    作者回复: 唉……想办法看看能不能把工具链先升级上去,并测试有没有问题吧。

    2019-12-26
    4
    1
收起评论
显示
设置
留言
33
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部