首先来看下这样一段简单的代码:
#include
template
typename sum(s1 x, s2 y)
{
typename z = x + y;
return z;
}
int main()
{
std::cout << sum(3,5) << std::endl;
return 0;
}
这段代码的目标是写一个函数模版,用来计算各种类型参数之和并返回。但是这里存在两个问题:函数模版sum中,形参x的类型为s1,形参y的类型为s2,那么,函数的返回值z(typename)和返回类型应该设为什么呢?假设s1的类型为short,s2的类型为int,那么x+y会进行整型提升,得到的结果是int型,返回类型应该用int,但是如果s1的类型是float呢?如果是结构和类呢?可能出现的情况太多了,没办法预先设定一个适用于所有情况的返回类型,所以针对这种情况,C++11提出了解决方案,那就是新增的decltype关键字
首先先简单示范一下decltype关键字的基本使用方法:
int a;
decltype (a) b;
int a声明了一个int类型的变量a,然后decltype (a)b,可以这样去理解:声明了一个与a类型一致的变量b ,把decltype (a)当成int这样的数据类型,a是什么类型,decltype (a)就是什么类型,它声明的b也就是相应的a的类型。好,明白了这个,我们再来熟悉一下它,看下面的例子:
int a = 10;
int & b = a;
decltype (b) c;
问c是什么类型呢?答案很简单,看b嘛,b是什么类型?是int &,那c就是int&类型啦,再看下一个例子:
long a = 10;
const long * b = &a;
decltype (b) c;
问这次c是什么类型呢?按照我们之前的理解,先看decltype后面跟的(b),b是一个被const修饰的long类型的指针,明白了,那c的类型就是const long *了嘛。
理解了上述内容后,再来看一下decltype更复杂的应用。理解这些总共可以分为四步,每一步对应着一种规则。做一个形象一点的比喻:decltype就是一条条鱼,四条规则对应着四层渔网,不论什么鱼总有一层渔网可以抓住它。以例句:decltype (x)y;为例,下面我们一起来看一下:
第一步(第一层渔网):
如果decltype后面括号括起来的内容(比如上面的(x))是一个标识符,那么y的类型与标识符的类型一致
是不是和前面理解的差不多,来个例子加深一下理解:
int a = 10;
int b = 20
int &c = a;
decltype (c) d = a;
问d是什么类型?有些人看到d=a就开始放飞想象力(没错,就是我了)了:“这里c的类型是int &,所以d应该是int &,但是后面接了个a,a的类型是int,=号把a赋给d了,所以d是int类型!”,但是其实不是,可以这样理解:把decltype (c)换成int,就变成了int d = a;这是赋值语句没错,赋值语句是把值赋给左值,不会赋类型呀。这里也一样。所以这段代码的意思是:把a的值10,赋给了c类型(int &)的变量d,变量d的类型还是int &,不会改变的。
第二步(第二层渔网):
如果decltype(x) y;中,x是一个函数调用,则y的类型为函数调用的返回类型
例如:
double suv(int a);
decltype(suv(5)) s1;//函数suv的返回类型为double,所以s1的返回类型为double
这个应该很好理解吧,函数返回int就是int类型,返回double就是double类型。
第三步(第三层渔网):
如果decltype (x)y;中,x是一个左值,那么y为指向其类型的引用
这种情况好像第一步就处理了啊,那么什么样的情况才会走到第三步来呢。其实也很简单,看(x)有几层括号就行了,1层括号走第一步,2层括号走这个第三步。举个栗子:
int a = 10;
decltype (a) b = a;//这里b的类型为int
decltype ((a)) c = a;//这里c的类型为int &
第四步(第四层渔网):
如果decltype (x)y;中,x不满足上述三步任何一步的条件,那么y的类型和x的类型就是一致的
举例说明一下:
int a1 = 10;
int a2 = 10;
int &b1 = a;
int &b2 = b;
decltype (b1+b2) c;
这里decltype (b1+b2)c;中,(b1+b2)不是标识符,也不是函数调用,更不是一个左值,它是一个表达式。编译器会去推导出这个表达式的值,然后把这个值的类型赋给c(注意,这里编译器只是推导,并不会去真正计算这个表达式,包括函数调用也是,编译器通过查看函数的原型后知道了返回值的类型再赋给变量),所以在这里,c的类型是int(不是int & 哦),因为编译器推导出表达式b1+b2的值就是int类型
好,了解到这里,就可以着手解决最初的函数模版问题了,可以这样做:
#include
template
typename sum(s1 x, s2 y)
{
decltype (x + y) z = x + y;
return z;
}
int main()
{
std::cout << sum(3,5) << std::endl;
return 0;
}
把函数模版sum中的z的类型设置为decltype (x + y)就可以了,这样不管x和y是什么类型,z的类型都是x+y后的类型,整形提升类型转换啊什么的通通不在话下。但是decltype关键字有个缺陷,不能用于函数名称前面的返回类型,因为写成这样的话:decltype(x + y)sum(s1 x,s2 y);参数x和y还没有声明,不在作用域内,编译器无法知道(x + y)是什么类型,这里就可以用到占位符“auto”了,再改一下代码:
#include
template
auto sum(s1 x, s2 y)->decltype (x + y)
{
decltype (x + y) z = x + y;
return z;
}
int main()
{
std::cout << sum(3,5) << std::endl;
return 0;
}
在原本应该声明函数返回类型的地方使用auto占位符,然后在函数形参声明之后用->符号给函数指定返回类型。就像你和朋友在学校饭堂打饭,档口前面排满了人,你们想知道今天食堂吃什么菜,来决定今天吃食堂还是吃大排档,于是你就让朋友在后面排队,你去前面看有什么菜,菜还行你就回来插到朋友面前继续排队,菜不好吃就不排了吃大排档去。auto就像你朋友,起到一个占位的用处,你就是decltype(x + y),溜到前面去看菜单(x + y)了,应该很好理解吧~
如有错漏,欢迎指正,感谢
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)