C++ 类型说明符auto和decltype

前言

随着程序越来越复杂,程序中用到的类型也越来越复杂,这种复杂性有两方面:

(1)一些类型难以“拼写”,它们的名字既难记住用容易出错,还无法明确体现其真实目的和含义;

(2)有时候根本搞不清楚到底需要什么类型,程序员不得不回头从程序的上下文寻求帮助。

这里主要针对(2)来分析。

 

auto

 

C++11引入auto类型说明符,用它就能让编译器替我们去分析表达式所属的类型。和原来那些只对应一种特定类型的说明符(比如double)不同,auto让编译器通过初始值来推算变量的类型。显然,auto定义的变量必须有初始值。

 

//由val1和val2相加的结果可以推断出item的类型

auto item = val1 + val2;          //item初始化val1和val2相加的结果

 

使用auto也能在一条语句中声明多个变量。因为一条声明语句只能有一个基本数据类型,所以该语句中的所有变量的初始基本数据类型必须都一样。

auto i = 0, *p = &i;    //正确,i是整数,p是整形指针

auto sz = 0, pi = 3.14;    //错误,sz和pi的类型不一致

 

复合类型、常量和auto

(1)编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则。

如:int i = 0, &r = i;

auto a = r;  //a是一个整数,(r是i的名别,而i是一个整数)

 

(2)其次,auto一般会忽略顶层const,同时底层const则会保留下来,比如当初始值是一个指向常量的指针时:

const int ci = i, &cr = ci;

auto b = ci;    //b是一个整数(ci的顶层const特性被忽略)

auto c = cr;    //c是一个整数(cr是ci的别名,ci本身是一个顶层const)

auto d = &i;   //d是一个整型指针(整数的地址就是指向整数的指针)

auto e = &ci;   //e是一个指向整型常量的指针(对常量对象取地址是一种底层const

如果希望推断出的auto类型是一个顶层const,需要明确指出:

const auto f = ci;     //ci的推演类型是int,f是const int

 

还可以将引用的类型设为auto,此时原来的初始化规则仍然适用

auto &g = ci;    //g是一个整型常量的引用,绑定到ci

auto &h = 42;   //错误,不能为非常量引用绑定字面值

const auto &j = 42;  //正确,可以为常量引用绑定字面值

设置一个类型为auto的引用时,初始值中的顶层常量属性仍然保留。

 

注意:要在一条语句中定义多个变量时,切记,符号&和*只从属于某个声明符,而非基本数据类型的一部分,因此初始值必须是同一种类型:

auto k = ci, &l = i;    //k是整数,l是整型引用

auto &m = ci, *p = &ci;    //m是对整型常量的引用,p是指向整型常量的指针

auto &n = i, *p2 = &ci;   //错误,i的类型是int, 而&ci的类型是const int

 

(这里什么叫顶层const,底层const,只要注意const可以表示任意的对象是常量,这一点对任何数据类型都适用,如算数类型、类、指针等;而底层const则与指针和引用等复合类型的基本类型部分有关)

const int i = 0;   // i是顶层const 

int *const p = &i;   // p是顶层const,不可以改变指针p

const int *d = &i;   //d是底层const,不可以改变其指向的内容

(区分指针是顶层const还是底层const,只要注意*在const的左右边,在const的左边就是顶层,在右边就是底层)

 

 

decltype类型指示符

有时希望从表达式的类型推断出要定义的变量的类型,但是不想用该表表达式的值初始化变量。为了满足这一要求,C++11新标准引人入了第二种类型说明符decltype,它的作用是选择并返回操作数的数据类型。在此过程中,编译器分析表达式,并得到它的类型,却不实际计算表达式的值。

decltype(f())  sum = x;  // sum的类型就是函数f的返回类型

编译器并不实际调用函数f,而是使用当调用发生时f的返回值类型作为sum的类型。换句话说,编译器为sum指定的类型是什么呢?就是假如f被调用的话将会返回的那个类型。

decltype处理顶层const和引用的方式与auto有些许不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内):

const int ci = 0, &cj = ci;

decltype(ci) x = 0;   // x的类型是const int

decltype(cj) y = x;   // y的类型是const int&,y绑定到变量x

decltype(cj) z;      //错误:z是一个引用,必须初始化

因为cj是一个引用,decltype(cj)的结果就是引用类型,引用必须被初始化。

 

需要注意的是:引用从来都是作为其所指对象的同义词出现,只用用在decltype处是一个例外。

 

decltype和引用

如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。有些表达式将向decltype返回一个引用类型。一般来说当这种情况发生时,意味着该表达式的结果对象能作为一条赋值语句的左值;

int i = 42, *p = &i, &r = i;

decltype(r + 0)  b;    //正确:加法的结果是int,因此b是一个未初始化的int

decltype(*p) c;    //错误:c是int &,必须初始化

为什么*p解引用操作,decltype返回类型是引用类型。解引用指针可以得到指针指向的对象,而且还能给这个对象赋值。因此,decltype(*p)的结果类型就是int &,而非int。。。(其实这个地方用decltype对左值求解结果是引用类型)

decltype和auto的另一处重要区别。decltype的结果类型与表达式密切相关。有一种情况需要注意:对于decltype所用的表达式,如果变量名加上了一对括号,则得到的类型与不加括号会有不同。如果decltype使用的是一个不加括号的变量,则得到的结果就是该变量的类型;如果给变量加上一对括号或多层括号,编译器就会把它当成是一个表达式。变量是一种可以作为赋值语句左值的特殊表达式,所以这样的decltype就会得到引用类型。

decltype((i)) d;    //错误:d是int &,必须初始化

decltype(i) e;    //正确,e是一个未初始化的int

切记:decltype((variable))的结果永远是引用,而decltype(variable)结果只有当variable本身就是一个引用时才是引用。

 

 

 auto与decltype区别举例

int ia[] = {0,1,2,3,4,5,6,7,8,9};    // ia是一个含有10个整数的数组

auto ia2(ia);    // ia2是一个整型指针,指向ia的第一个元素

ia2 = 42;     // 错误:ia2是一个指针,不能用int值给指针赋值

尽管ia是由10个整数构成的数组,但当使用ia作为初始值时,编译器实际执行的初始化过程类似下面的形式:

auto ia2(&ia[0]);

但是当使用decltype关键字时,上述转换不会发生,decltype(ia)返回的类型是由10个整数构成的数组:

decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9};    // ia3是一个含有10个整数的数组

ia3 = p;      // 错误:不能用整型指针给数组赋值

ia3 [4] = i;   //正确:把i的值赋给ia3的一个元素

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值