变量声明的注意事项。

Item 5:Prefer auto to explicit type declarations

这一条条款全篇都在强调:为什么我们更青睐使用auto而不是显示类型声明。总得来说,好处有四个:

(1)避免潜在未初始化变量的出现

在C++中有可能因为定义了变量却没有初始化导致错误的结果。而且编译器也不报错。

int x1;                //不报错,但潜在的未初始化的变量
auto x2;            //错误!必须要初始化
auto x3=0;            //没问题,x已经定义了

(2)省略冗长的声明类型

std::map<int,int>::iterator it=mymap.begin(); //复杂
auto it=mymap.begin(); //简单

(3)直接保存闭包

所谓闭包通俗点说就是指lambda表达式、重载运算符、bind表达式。

自从C++11后,我们可以用auto来存储lambda表达式:

auto derefUPLess = [](const std::unique_ptr<Widget> &p1,const std::unique_ptr<Widget> &p2)
{
    return *p1<*p2;
};//专用于Widget类型的比较函数

在C++14,lambda表达式也可以使用auto:

auto derefUPLess = [](const auto& p1,const auto& p2)
{
    return *p1<*p2;
};

上述例子中,我们使用auto来保存这个闭包(lambda表达式)。如果不使用auto就必须使用std::function

std::function是一个C++11标准模板库中的一个模板,它泛化了函数指针的概念。与函数指针只能指向函数不同,std::function可以指向任何可调用对象,也就是那些像函数一样能进行调用的东西。当你声明函数指针时你必须指定函数类型(即函数签名),同样当你创建std::function对象时你也需要提供函数签名

bool(const std::unique_ptr<Widget> &p1,
     const std::unique_ptr<Widget> &p2);

就需要使用std::function这么写:

std::function<bool(const std::unique_ptr<Widget> &p1,
                   const std::unique_ptr<Widget> &p2)> func;

如果不用auto,上面那么大一坨东西十分繁琐!

(4)避免类型不匹配

这里举两个常见的错误,他们都可以被auto很友好的规避。

错误1:误以为std::vector::size_type就是unsigned

std::vector<int> v;
unsigned sz = v.size();

Windows 32-bitstd::vector::size_typeunsigned int都是一样的类型,但是在Windows 64-bitstd::vector::size_type是64位,unsigned int是32位。这意味着这段代码在Windows 32-bit上正常工作,但是当把应用程序移植到Windows 64-bit上时就可能会出现一些问题。

错误2:std::unordered_map的key是一个常量

std::unordered_map<std::string,int> m;
...
for(const std::pair<std::string,int>& p : m)
{
    ...
}

由于是常量,所以上面的调用应该改为std::pair

Item 6:Use the explicitly typed initializer idiom when auto deduces undesired types.

auto推断也存在一些问题。

现在有一个函数feature,传入一个vector数组,比较每个元素正负,然后返回vector

std::vector<bool> features(vector<int> nums)
{
    vector<bool> res;
    for (auto x : nums)
        res.push_back(x > 0 ? true:false);
    return res;
}

现在我想返回数组容器的第5个元素的正负情况,我这么来调用:

auto negative4 = features(nums)[4];

当把鼠标移到negative4上查看类型时,结果….

vectoroperator[]的返回值并不是bool类型却变为了std::vector::reference,这样的话,后面的调用一定会出现奇奇怪怪的问题,这是怎么回事呢?

  • 因为bool占用一个字节,标准库为了节省内存,改用bit来表示
  • 因为operator[]需要返回一个内部元素的引用,但是没办法对一个bit进行引用
  • 为了让返回的类型统一,无论是bool类型,还是其它类型

此标准库为了实现上述三个目标就封装了一个内部的类型vector::reference,是一个proxy类(代理类)。其实这个问题指针对于bool类型,如果是实数类型,不会受到任何影响

解决的办法是调用显式类型初始器!通过强制转化达成目的。

auto negative4 = static_cast<bool>(features(nums)[4]);