C++11 类型推导auto

在C++11之前,auto关键字用来指定存储期。在新标准中,它的功能变为类型推断。auto现在成了一个类型的占位符,通知编译器去根据初始化代码推断所声明变量的真实类型。
使用auto会拖慢c++效率吗?完全不会,因为在编译阶段编译器已经帮程序员推导好了变量的类型。
使用auto会拖累C++编译效率吗?完全不会,因为在auto出现之前C++需要先推导等号右侧表达式的类型,然后检查它与变量的类型是否可以转换(兼容转换、向下类型转换和自定义类型转换)。auto出现之后,C++在推导出等号右侧表达式的类型之后,直接指定给变量。


auto并非一种类型声明,而是类型声明时的占位符,编译器在编译时期会将auto替代为变量的实际类型。因此不能和typeid,sizeof一起使用。

1.使用auto通常意味着更短的代码(除非你所用类型是int,它会比auto少一个字母)

试想一下当你遍历STL容器时需要声明的那些迭代器(iterator),现在不需要去声明那些typedef就可以得到简洁的代码了。

std::map<std::string, std::vector<int>> map;
for(auto it = begin(map); it != end(map); ++it) {}

2.返回值占位符

auto不能用来声明函数的返回值,但如果函数有一个尾随的返回类型时,auto是可以出现在函数声明中返回值位置。这种情况下,auto并不是告诉编译器去推断返回类型,而是指引编译器去函数的末端寻找返回值类型。在下面这个例子中,函数的返回值类型就是operator+操作符作用在T1、T2类型变量上的返回值类型。

template <typename T1, typename T2>
auto compose(T1 t1, T2 t2) -> decltype(t1 + t2)
{
   return t1+t2;
}
auto v = compose(2, 3.14); // v's type is double

3.auto声明的变量必须初始化

int main()
{
    auto m = 1; 
    auto n{1};
    auto z = new auto(10);
    
    auto a;                 //错误,未初始化
    auto int b = 1;         //错误,auto不能和其它类型一起使用(C++98和C中可以)
    auto c = new auto();    //错误,没有初始化
    auto d = 1, e = 2.01;   //错误,定义在一个auto序列的变量必须始终推导成同一类型
    
    return 0;
}

4.C++标准中规定auto可以与CV(const & volatile qualifiers)限制符一起使用,不过声明为auto的变量不能从其初始化表达式中带走CV限制符

从const变量推导或用auto接收从函数返回的const变量,会丢失const属性,除非显示声明为引用类型。

从引用变量推导或用auto接收从函数返回的引用变量,会丢失引用特性,除非显示声明为引用类型。

#include <iostream>
#include <typeinfo>
#include <vector>

using namespace std;

struct student
{
    std::string name;
    int age;
    student(std::string name, int age)
    : name(name)
    , age(age)
    {
        std::cout << "constructor" << std::endl;
    }

    student(const student& rhs)
    {
        std::cout << "copy constructor" << std::endl;
        this->name = rhs.name;
        this->age = rhs.age;
    }
};

student& backRef(student& s)
{
    return s;
}

int main()
{
    const auto var1 = 1;        //var1为const int
    auto var2 = var1;           //var1为int(除非var2声明为:auto& var2类型)
    auto& var3 = var1;          //var3为const int
    var2 = 3;                   //经过auto推导,var2变量已经丢失了const属性
    //var3 = 5;                 //错误,const变量禁止修改

    auto var4 = 4;
    auto& var5 = var4;          //var5是var4的引用类型
    var5 = 6;
    auto var6 = var5;           //从var5中推导,只会获得变量类型,会丢失引用特性
    var6 = 10;
    std::cout << "var4:" << var4 << std::endl;
    std::cout << "var5:" << var5 << std::endl;
    std::cout << "var6:" << var6 << std::endl;

    student stu1{"sanz", 10};
    auto stu2 = stu1;

    auto stu3 = backRef(stu1);  //函数返回引用类型,但实际会调用拷贝构造函数
    stu3.age = 12;
    auto& stu4 = backRef(stu1); //显示添加引用后,还是stu1
    stu4.age = 18;
    std::cout << stu1.age << std::endl;
    std::cout << stu2.age << std::endl;
    std::cout << stu3.age << std::endl;
    std::cout << stu4.age << std::endl;

    return 0;
}
View Code

 

auto也不是万能的,受制于语法的二义性,或者是实现的困难性,auto往往也有使用上的限制。

#include <iostream>
#include <typeinfo>
#include <vector>

using namespace std;

struct str
{
    auto var = 10;      //1.a通过编译auto非静态成员变量,无法
};

void fun(auto x)        //2.auto函数参数,无法通过编译
{
}

int main()
{
    int x[3];
    auto y = x;
    auto z[3] = x;      //3.auto数组,无法通过编译
    
    //auto模版参数,无法通过编译
    vector<auto> v = {1};
    
    return 0;
}

分析一下上述4中不能推导的情况:

1.对于函数fun来说,auto不能是其形参类型。由于其有默认参数,所以应该推导fun形参x的类型为int型。但事实无法符合大家的想象,如果程序员需要泛型的参数,还是求助与模版。

2.对于结构体或类来说,非静态成员变量的类型不能为auto。编译器阻止auto对结构体中的非静态成员进行推导,即使成员拥有初始值。

3.声明auto数组。x是一个数组,y类型是可以推导的,而声明auto z[3]这样的数字同样会被编译器禁止。

4.在实例化模版的时候使用auto作为模版参数,虽然读者认为这里一眼而知是int类型,但编译器却阻止了编译。

posted @ 2017-03-08 17:46  滴水瓦  阅读(2333)  评论(0编辑  收藏  举报