【C++】五分钟快速理解decltype与auto

【C++】五分钟快速理解decltype与auto,第1张

首先来看下这样一段简单的代码:

#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)了,应该很好理解吧~

 

如有错漏,欢迎指正,感谢

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/707569.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-24
下一篇 2022-04-24

发表评论

登录后才能评论

评论列表(0条)

保存