C++11新特性之一:auto
扫描二维码
随时随地手机看文章
C++是一门伟大的语言,永远给程序员最大的设计自由, 未使用的特性从不产生副作用,新版本永远完全兼容旧版本。 C++11先前被称作C++0x,即ISO/IEC 14882:2011,是C++编程语言的一个标准。
之前的C++标准包括C++98、C++03。 虽然此后的[C++14]才是C++的现行标准,但C++14旨在对C++11的小扩展(漏洞修复、功能改进),而C++11仍然是一个具有热度的关键词。
C++98 auto
早在C++98标准中就存在了auto关键字,那时的auto用于声明变量为自动变量,自动变量意为拥有自动的生命期,这是多余的,因为就算不使用auto声明,变量依旧拥有自动的生命期:
int a =10 ;//拥有自动生命期 auto int b = 20 ;//拥有自动生命期 static int c = 30 ;//延长了生命期
C++98中的auto多余且极少使用,C++11已经删除了这一用法,取而代之的是全新的auto:变量的自动类型推断。
C++11 auto
auto可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型,类似的关键字还有decltype。举个例子:
auto a = 10; auto *pa = new auto(a); auto **rpa = new auto(&a); cout << typeid(a).name() << endl; //输出:int cout << typeid(pa).name() << endl; //输出:int * cout << typeid(rpa).name() << endl; //输出:int **
typeid运算符可以输出变量的类型。
这种用法就类似于C#中的var关键字。auto的自动类型推断发生在编译期,所以使用auto并不会造成程序运行时效率的降低。而是否会造成编译期的时间消耗,我认为是不会的,在未使用auto时,编译器也需要得知右操作数的类型,再与左操作数的类型进行比较,检查是否可以发生相应的转化,是否需要进行隐式类型转换。
auto的用法
上面举的这个例子很简单,在真正编程的时候也不建议这样来使用auto,直接写出变量的类型更加清晰易懂。下面列举auto关键字的正确用法。
用于代替冗长复杂、变量使用范围专一的变量声明
想象一下在没有auto的时候,我们操作标准库时经常需要这样:
#include#includeint main() { std::vectorvs; for (std::vector::iterator i = vs.begin(); i != vs.end(); i++) { //... } }
这样看代码写代码实在烦得很。有人可能会说为何不直接使用using namespace std,这样代码可以短一点。实际上这不是该建议的方法(C++Primer对此有相关叙述)。使用auto能简化代码:
#include#includeint main() { std::vectorvs; for (auto i = vs.begin(); i != vs.end(); i++) { //.. } }
for循环中的i将在编译时自动推导其类型,而不用我们显式去定义那长长的一串。
在定义模板函数时,用于声明依赖模板参数的变量类型
#include#includeusing namespace std; templatevoid add(T t, U u) { auto s = t + u; cout << typeid(s).name() << endl;//输出:double } int main() { // 使用模板技术时,如果某个变量的类型依赖于模板参数,使用auto确定变量类型 add(101, 1.1); system("pause"); return 0; }
若不使用auto变量来声明s,那这个函数就难定义啦,不到编译的时候,谁知道x*y的真正类型是什么呢?
函数返回占位符
在这种情况下,auto主要与decltype关键字配合使用,作为返回值类型后置时的占位符。此时,关键字不表示自动类型检测,仅仅是表示后置返回值的语法的一部分。
templateauto add(T t, U u) -> decltype(t + u) { return t + u; }
auto在这里的作用也称为返回值占位,它只是为函数返回值占了一个位置,真正的返回值是后面的decltype(t + u)。为何要将返回值后置呢?如果没有后置,则函数声明时为:
templatedecltype((*(T*)0) + (*(U*)0)) add(T t, U u) { return t + u; }
此时虽然能实现相同的功能,但是代码编写要丑陋得多。
注意事项
1.auto 变量必须在定义时初始化,这类似于const关键字
定义在一个auto序列的变量必须始终推导成同一类型。例如:
auto a4 = 10, a5 = 20, a6 = 30;//正确 auto b4 = 10, b5 = 20.0, b6 = 'a';//错误,没有推导为同一类型
2.如果初始化表达式是引用,则去除引用语义
int a = 10; int &b = a; auto c = b;//c的类型为int而非int&(去除引用) auto &d = b;//此时c的类型才为int& c = 100;//a =10; d = 100;//a =100;
3.如果初始化表达式为const或volatile(或者两者兼有),则除去const/volatile语义
const int a1 = 10; auto b1= a1; //b1的类型为int而非const int(去除const) const auto c1 = a1;//此时c1的类型为const int b1 = 100;//合法 c1 = 100;//非法
4.如果auto关键字带上&号,则不去除const语意
const int a2 = 10; auto &b2 = a2;//因为auto带上&,故不去除const,b2类型为const int b2 = 10; //非法
这是因为如何去掉了const,则b2为a2的非const引用,通过b2可以改变a2的值,则显然是不合理的。
5.初始化表达式为数组时,auto关键字推导类型为指针
int a3[3] = { 1, 2, 3 }; auto b3 = a3; cout << typeid(b3).name() << endl;//输出int*
6.若表达式为数组且auto带上&,则推导类型为数组类型
int a7[3] = { 1, 2, 3 }; auto & b7 = a7; cout << typeid(b7).name() << endl;//输出为int[3]
7.函数或者模板参数不能被声明为auto
void func(auto a) //错误 { //... }
8.时刻要注意auto并不是一个真正的类型
auto仅仅是一个占位符,它并不是一个真正的类型,不能使用一些以类型为操作数的操作符,如sizeof或者typeid。
cout << sizeof(auto) << endl;//错误 cout << typeid(auto).name() << endl;//错误