现代C++实战30讲
吴咏炜
前 Intel 资深软件架构师
立即订阅
3686 人已学习
课程目录
已更新 14 讲 / 共 30 讲
0/4登录后,你可以任选4讲全文学习。
课前必读 (2讲)
开篇词 | C++这么难,为什么我们还要用C++?
免费
课前必读 | 有关术语发音及环境要求
基础篇 (9讲)
01 | 堆、栈、RAII:C++里该如何管理资源?
02 | 自己动手,实现C++的智能指针
03 | 右值和移动究竟解决了什么问题?
04 | 容器汇编 I:比较简单的若干容器
05 | 容器汇编 II:需要函数对象的容器
06 | 异常:用还是不用,这是个问题
07 | 迭代器和好用的新for循环
08 | 易用性改进 I:自动类型推断和初始化
09 | 易用性改进 II:字面量、静态断言和成员函数说明符
提高篇 (3讲)
10 | 到底应不应该返回对象?
11 | Unicode:进入多文字支持的世界
12 | 编译期多态:泛型编程和模板入门
现代C++实战30讲
登录|注册

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

吴咏炜 2019-12-13
你好,我是吴咏炜。
在之前的几讲里,我们已经多多少少接触到了一些 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/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《现代C++实战30讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(14)

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

    作者回复: 识货。😇

    毕竟这个专栏的篇幅是 30 讲,不是 60 讲或 100 讲啊。

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

    作者回复: 是说如果一个类Obj既有:

    Obj(initializer_list<int>);

    又有:

    Obj(double);

    那你想调用后面那个构造函数,就别用 Obj{1.0} 这种形式,而用 Obj(1.0)。

    2019-12-14
    1
  • Qi
    还在用Ti很旧的arm板子做开发,工具链都不支持C++11了,还是坚持看到现在了。。。

    作者回复: 坚持向前看啊😄

    看看工具链有没有升级版本可以支持更新的标准?

    2019-12-23
  • 常常要奋斗

    “这个语法主要的限制是,如果一个构造函数既有使用初始化列表的构造函数,又有不使用初始化列表的构造函数”,应该是如果一个类既有...又有...~

    作者回复: 哈哈,真是的。谢谢。回头改一下。

    2019-12-22
  • lyfei
    谢谢老师上次耐心的回复。
    上次问题: 就是我在编译文稿中的推导向导的时候,提示错误:class template argument deduction failed:

    MyObj(const char*) -> MyObj<std::string>;

    我是将推导向导写在了函数体内部,导致报错,如果写在函数体外部是正常的,所以我有个疑问:
    为什么写在函数体外部是可以的呀?
    写在函数体外部和内部给编译器带来了什么区别?(为什么写在函数内部就报错了😂)
    谢谢老师的讲解

    作者回复: “龟腚”而已。参考资料 [4a] 里有的:

    “用户定义推导指引必须指名一个类模板,且必须在类模板的同一语义作用域(可以是命名空间或外围类)中引入,而且对于成员类模板,必须拥有同样的访问,但推导指引不成为该作用域的成员。”

    2019-12-18
    2
  • lyfei
    老师您好,我对下面这两个疑惑有些不解:
    1. 就是我在编译文稿中的推导向导的时候,提示错误:class template argument deduction failed:
    MyObj(const char*) -> MyObj<std::string>;
    2. MyObj obj2{"hello"}; 这句话编译器自动推断出来的类型是:MyObj<char const*> 而不是文稿中注释的MyObj<const char*>

    编译器:g++7.4.0

    作者回复: 1. 文稿中不是完整的代码。我拿下面的完整代码测试是没有问题的:

    #include <string>

    using namespace std;

    template <typename T>
    struct MyObj {
        MyObj(T value)
            : value_(value) {}
        T value_;
    };

    MyObj(const char*) -> MyObj<string>;

    int main()
    {
        MyObj obj{"hello"};
    }

    2. const char* 就是 char const*,没有区别,是同一个东西,都是指向常字符的指针(指针指向的内容不可更改)。如果写成 char* const,那就不一样了——那是指向字符的常指针(指针本身不可更改,指向的内容可更改)。

    2019-12-17
    1
  • lyfei
    老师您好,就是您文稿中的代码:
    template <typename T>void foo(const T& c){ using std::begin; using std::end; // 使用依赖参数查找(ADL);见 <span class="orange">[2] for (auto it = begin(c), ite = end(c); it != ite; ++it) { // 循环体 }}
    我这里有个疑惑,就是这里哪一句可以体现出ADL呀?(ADL我理解的是:编译器根据传入的参数来推断出该命名空间中正确的函数)
    谢谢老师的回复

    作者回复: 就是 begin 和 end。对象 c 所属类型所在的名空间里的这两个函数将被优先使用。

    2019-12-17
  • 墨梵
    吴老有没有打算在网络编程和多线程这几个点上做一个剖析呢?

    作者回复: 内容太多,这两个话题都会讨论到,但可能不会太深。具体参见目录。第 19、20、27 讲。

    2019-12-16
  • 海生
    目前的话,c11用的比较多,c17估计大多数以前的老久代码都是不支持的。bind 和functional 实现类似Java的面向interface编程的方式比auto应用影响更大吧,毕竟c 是强语言,类型声明是应该的义务。老师后续能不能讲讲进程编程和多线程,CAS,disruptor类的。algorithm 库里面的东西也很多,值得讲讲。

    作者回复: C++,不是 C。这是两种不同的语言。

    这个专栏讲的内容是比较确定的,你可以看目录。后面我会讲到函数式和多线程,CAS 可以稍微讲一下。其他内容大概不会覆盖到了……

    算法本身很零散,又不算难理解。在我讲到的个别算法之外,其他大家自己看应该不会很复杂。

    2019-12-14
  • 小一日一
    由于维护优化的是公司10年前的老代码,gcc版本停留在了古老的4.8.5,我在写新项目和新特性时只能使用C++11特性,老师今天讲的C++11引入的所有特性我都在使用,如数据成员的默认初始化,统一初始化,列表初始化,后置返回值类型,decltype,auto,而C++14和17引入的结构化绑定,类模板的模板参数推导,decltype(auto)无法使用,只有望洋兴叹,留口水的份。

    我想问一下,别人是否也有我一样由于编译器受限无法使用C++新特性的情况?

    作者回复: 先升级编译器,解决任何编译问题,再用测试来确保没有问题。

    编译期和语言对向后兼容性一直保持得很好的,原则上不应该有问题。不能太保守了。(但也不要激进地每个新版本都升。)

    2019-12-13
    4
  • 皓首不倦
    老师您好 我记得以前自己对auto的推导进行学习的时候 想看推导出的到底是什么类型 需要用boost库的一些特殊api 才行 auto推出来到底什么时候带引用 什么时候不带引用有时记不清楚 希望能直接把auto推出来的类型名字包括带不带引用符号打出来看下 请问下只用标准库的api 的话 有什么方便的方法能把一个变量的完整类型信息打印出来看吗

    作者回复: Boost 也没什么特别神秘的方法吧。不用 Boost,方法也应该相似的。

    我个人一般用 Scott Meyers 教的一个办法:

    #define TYPE_DISPLAY(var) \
        static type_displayer<decltype(var)> type_display_test

    template <typename T> // declaration only for type_displayer;
    class type_displayer;

    用的时候,就写 TYPE_DISPLAY(变量名字);。

    2019-12-13
    2
  • Cheng
    for(auto &it : list)
             {
     
             }
    这个用法不知后面是否有讲到?

    作者回复: 这个已经讲到过了。不会再单独讲。

    你上面的变量命名有问题,会让人误以为 it 是个迭代器。它只是元素的引用,并不是迭代器。

    2019-12-13
  • hello world
    初始化列表那里还没怎么看明白唉,还是得多补习补习

    作者回复: 多自己试验例子来体会一下。

    2019-12-13
  • 我叫bug谁找我
    想知道auto到底什么情况下用,什么情况下不要用auto,用多了会不会造成阅读困难

    作者回复: 代码怎么看起来好看怎么用。😂

    2019-12-13
    2
收起评论
14
返回
顶部