【Effective Modern C++】第2章 auto

【Effective Modern C++】第2章 auto

auto好处:少打一些字,阻止那些由于手动指定型别带来的潜在错误和性能问题

条款5:优先选用auto,而非显示型别声明

  • 使用auto必须初始化,就可以避免一系列由未初始化带来的问题

  • auto用在lambda表达式的形参类型声明中,并且可以声明变量直接持有闭包,如:

    auto derefLess =                                //C++14版本
        [](const auto& p1,                          //被任何像指针一样的东西
           const auto& p2)                          //指向的值的比较函数
        { return *p1 < *p2; };
    
  • 你可能会想我们完全不需要使用auto声明局部变量来保存一个闭包,因为我们可以使用std::function对象。

    std::function<bool(const std::unique_ptr<Widget> &,
                       const std::unique_ptr<Widget> &)>
    derefUPLess = [](const std::unique_ptr<Widget> &p1,
                     const std::unique_ptr<Widget> &p2)
                    { return *p1 < *p2; };
    

    语法冗长不说,还需要重复写很多形参类型,使用std::function还不如使用auto。用auto声明的变量保存一个和闭包一样类型的(新)闭包,因此使用了与闭包相同大小存储空间。实例化std::function并声明一个对象这个对象将会有固定的大小。这个大小可能不足以存储一个闭包,这个时候std::function的构造函数将会在堆上面分配内存来存储,这就造成了使用std::functionauto声明变量会消耗更多的内存。换句话说,std::function方法比auto方法要更耗空间且更慢,还可能有out-of-memory异常。

  • auto还可以避免一类“型别捷径”的问题。v.size()的标准返回类型是std::vector<int>::size_type,但是只有少数开发者意识到这点。std::vector<int>::size_type实际上被指定为无符号整型,所以很多人都认为用unsigned就足够了,举个例子,在Windows 32-bitstd::vector<int>::size_typeunsigned是一样的大小,但是在Windows 64-bitstd::vector<int>::size_type是64位,unsigned是32位。这意味着这段代码在Windows 32-bit上正常工作,但是当把应用程序移植到Windows 64-bit上时就可能会出现一些问题。谁愿意花时间处理这些细枝末节的问题呢?所以使用auto可以确保你不需要浪费时间:

    auto sz =v.size();                      //sz的类型是std::vector<int>::size_type
    

    再比如,考虑下面的代码,看起来好像很合情合理的表达,但是这里有一个问题,你看到了吗?

    std::unordered_map<std::string, int> m;for(const std::pair<std::string, int>& p : m)
    {//用p做一些事
    }
    

    要想看到错误你就得知道std::unordered_mapkeyconst的,所以hash tablestd::unordered_map本质上的东西)中的std::pair的类型不是std::pair<std::string, int>,而是std::pair<const std::string, int>。但那不是在循环中的变量p声明的类型。编译器会努力的找到一种方法把std::pair<const std::string, int>(即hash table中的东西)转换为std::pair<std::string, int>p的声明类型)。它会成功的,因为它会通过拷贝m中的对象创建一个临时对象,这个临时对象的类型是p想绑定到的对象的类型,即m中元素的类型,然后把p的引用绑定到这个临时对象上。在每个循环迭代结束时,临时对象将会销毁,如果你写了这样的一个循环,你可能会对它的一些行为感到非常惊讶,因为你确信你只是让成为p指向m中各个元素的引用而已。

    使用auto可以避免这些很难被意识到的类型不匹配的错误:

    for(const auto& p : m)
    {//如之前一样
    }
    

    这样无疑更具效率,且更容易书写。而且,这个代码有一个非常吸引人的特性,如果你获取p的地址,你确实会得到一个指向m中元素的指针。在没有auto的版本中p会指向一个临时变量,这个临时变量在每次迭代完成时会被销毁。

  • 关于使用auto代替传统类型声明对源码可读性的影响的问题,放松,auto可选项,不是命令,在某些情况下如果你的专业判断告诉你使用显式类型声明比auto要更清晰更易维护,那你就不必再坚持使用auto

  • 事实是显式指定类型通常只会引入一些微妙的错误,无论是在正确性还是效率方面。而且,如果初始化表达式的类型改变,则auto推导出的类型也会改变,这意味着使用auto可以帮助我们完成一些重构工作。

要点速记

  • auto变量必须初始化,通常它可以避免一些移植性和效率性的问题,也使得重构更方便,还能让你少打几个字。
  • auto型别的变量都有着条款2和条款6中所述的毛病。

条款6:当auto推导的型别不符合要求时,使用带显式型别的初始化物习惯用法

  • std::vector<bool> features(const Widget& w);
    //bool highPriority = features(w)[5];
    auto highPriority = features(w)[5];		// !!!,highPriority的型别不再是bool了
    processWidget(w,highPriority);          //未定义行为!
    

    虽然从概念上来说std::vector<bool>意味着存放bool,但是std::vector<bool>operator[]不会返回容器中元素的引用(这就是std::vector::operator[]可返回除了bool以外的任何类型),取而代之它返回一个std::vector<bool>::reference的对象(一个嵌套于std::vector<bool>中的类)。std::vector<bool>::reference之所以存在是因为std::vector<bool>规定了使用一个打包形式(packed form)表示它的bool,每个bool占一个bit。那给std::vectoroperator[]带来了问题,因为std::vector<T>operator[]应当返回一个T&,但是C++禁止对bits的引用。

    无法返回一个bool&std::vector<bool>operator[]返回一个行为类似于bool&的对象。要想成功扮演这个角色,bool&适用的上下文std::vector<bool>::reference也必须一样能适用。在std::vector<bool>::reference的特性中,使这个原则可行的特性是一个可以向bool的隐式转化。(不是bool&,是**bool**。)

  • std::vector<bool>::reference是一个代理类(proxy class)的例子:所谓代理类就是以模仿和增强一些类型的行为为目的而存在的类。一些代理类被设计于用以对客户可见。比如std::shared_ptrstd::unique_ptr。其他的代理类则或多或少不可见,比如std::vector<bool>::reference就是不可见代理类的一个例子,还有它在std::bitset的胞弟std::bitset::reference
    一个普遍的规律是,“隐形”代理类和auto无法和平共处。这种类的对象往往会设计成仅仅维持到单个语句之内存在。所以以下的代码可能会导致未定义的i行为,要防止写出这样的代码:

    auto someVar="隐形"代理型别表达式
    

    那么如何发现使用了代理类呢,一是查看接口文档,二是查看头文件的函数签名。这个问题并不是放弃使用auto的理由,解决方案应该是进行另一次型别转换,如使用static_cast就可以表明“我故意转为了某种类型”。

    auto index = static_cast<int>(d * size());
    

要点速记

  • “隐形”的代理类可能会使auto从表达式中推导出“错误的”类型
  • 显式类型初始器惯用法强制auto推导出你想要的结果

参考:Effective Modern C++(中文版)和这里

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值