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; }
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类型,但编译器却阻止了编译。