C++11中的新关键字:auto与decltype

来源:互联网 发布:十字锈软件 编辑:程序博客网 时间:2024/04/29 22:08

auto

C++11之前,最无用的关键字

我们知道,在C++11以前,auto关键字的作用是声明函数内的局部变量为自动的变量。也就是说,它的作用是指出当前的变量为局部变量。是不是很多余?在函数里面声明整型局部变量,直接用 int i 就可以了,几乎没有人写成“规范”的 auto int i。所以auto成为了C++11之前最无用的关键字。


但是C++11标准来了,auto以前的作法全部作废。auto在新标准中被赋予新义,被用于自动类型推断。使用它,让编译器通过初始值来替我们去推断变量的类型。显然,auto定义的变量必须赋有初始值。比如:

auto i = 0, *p = &i;//正确:i是整数,p是整形指针auto sz = 0, pi = 3.14;//错误:sz和pi的类型不一致

首先需要明确一点的是,对于引用类型,auto看到的是引用的对象,也就是说,在参与auto判断类型的是引用对象的值,而不是这个引用。但是对于指针,和引用是不一样的,auto看到的仍然是这个指针。其次,auto一般会忽略顶层const,而保留底层const

举个例子就清楚了:

int i = 0;const int ci = i;const int &cr = ci;const int* icptr = &ci;auto b = ci;//auto是int,即 b是一个整数(ci的顶层const特性被忽略掉了)auto c = cr;//auto是int (虽然cr是底层const,但是auto看的是它实际引用的值,即ci,而ci只是顶层const)auto c1 = icptr;//auto是const int *auto d = &i;//auto是int*,d是一个整形指针,指向iauto e = &ci;//audo等价于const int*(对于常量对象取地址,得到的也是一种底层const)

所以说,如果希望auto推断出的类型具有顶层const属性,则需要手动显示指出:

int i = 0;const auto f = &i;//f的类型为 int * constf++;//error: increment of read-only variable 'f'|

对于auto手动显式指定的引用类型,如下例所示:

const int ci = 0;auto &g = ci;//auto为const int ,g具有底层const属性auto &h = 42;//error:不能为非常量引用绑定字面值auto const &j = 42;//正确:可以为常量引用绑定字面值

对于auto,还有两点要说明:

  • auto 不能做为模板参数。
因为auto 需要由初始化表达式来推导类型。尽管Bjarne Stroustrup认为这种用法应该被允许,但是编译器厂商因实现的难度太大而明确予以拒绝。不过,与其写成第一个,为何不直接写成第二个呢?

//1stvector<auto>* autoVector = new vector<int>;//2ndauto autoVector = new vector<int>;
显然,第二个才能体现 auto 的意义和存在价值。

  • auto 不能做为函数的参数类型和返回类型。
函数在编译时要name mangling,此时便需要确定参数的类型。声明为 auto 的话就无法确定了。因为函数返回值是右值,所以返回类型更不可以是auto。



decltype

decltype就是 declared type 的缩写。
如果希望表达式的类型去推导出要定义的变量的类型,但是不想用该表达式去初始化变量,那么就要用到C++11中新引入的关键字decltype了。它的作用是选择并返回操作数的数据类型。如下所示:

decltype(func()) sum = x;//sum的类型就是函数func()的返回类型
编译器并不会去调用函数func(),而只是根据func的原型去得到其返回类型。

decltype处理顶层const和引用的方式与auto有些不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型,包括顶层const和引用在内。而auto对于引用,看到的是引用指向的对象,同时忽略顶层const。

const int ci = 0,&cj = ci;decltype(ci) x = 0;//x的类型是const intdecltype(cj) y = x;//y的类型是const int&decltype(cj) z;//error:z是一个引用,但未初始化

再次强调一下,注意区别与auto的不同,对于auto来说,引用一直是作为其所指对象的同义词出现的,只有用在decltype处是个例外。

int i = 10;const int& iref = i;decltype(iref) i2ref = i;i2ref++;//error: increment of read-only reference 'i2ref'auto i3ref = iref;i3ref++;//okcout << "i = " << i << endl;cout << "i3ref = " << i3ref << endl;
输出结果为:

i = 10
i3ref = 11

可以看到,i2ref的类型是const int& ,而i3ref的类型是int .


如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。如果该表达式是一个左值,则返回引用类型。表达式如果是一个右值,则返回普通类型。
注意,一个变量如果加上一个括号,就变成一个表达式了

注意区分 ++x 与 x++。前者是左值表达式,后者是右值表达式。前者修改自身值,并返回自身;后者先创建一个临时对像,并用 x 的值赋之,后将修改 x 的值,最后返回临时对像。

左值和右值

说到左值与右值,这里需要区分一下:
在C语言里面,左值可以位于赋值语句的左侧,右值则不能。
但是在C++语言中,二者的区别就没那么简单了。
  • 首先,变量是左值
  • 赋值运算符得到的结果仍然是一个左值
  • 取地址运算符作用于一个左值运算对象,返回一个指向该运算对象的指针,这是一个右值
  • 内置解引用运算符、下标运算符、迭代器解引用运算符、string和vector的下标运算符的求值结果都是左值
  • 内置类型和迭代器的递增递减运算符作用于左值运算对象,其前置版本所得结果为左值,其后置版本所得结果为右值


前导函数 ( Forwarding Function)

最后,还有一个知识点也要讲:前导函数(Forwarding Function),多用于函数模板的返回值类型推导。
如下例中的问题:请问?type? 这个地方应该填入什么?

template<typename T1,class T2>?type? gt(T1 x, T2 y){//some codereturn x+y;}
对于上述模板函数的返回类型,我们似乎可以设置为decltype(x+y),但是不幸的是,在函数开头处,还未声明x和y,它们不在作用域内。
error: 'x' 'y' was not declared in this scope
因此,必须在声明参数后才能使用decltype(x+y)。

为此,C++引入了一种新的声明和定义函数的语法:
auto func(int x, float y) -> double

也可以写为:
auto func(int x, float y) -> decltype(x+y)
其中,-> double被称为后置返回odga(trailing return type)。

现在,对于刚才那个模板函数碰到的问题,有了解决方案:

template<typename T1,class T2>auto gt(T1 x, T2 y)-> decltype(x+y){//some codereturn x+y;}




0 0
原创粉丝点击