C++11类型推导的关键字auto和decltype_c++ 类型推导关键字-程序员宅基地

技术标签: c++  C++11  编程语言  

他们的语法格式:

auto varname = value;  
decltype(exp) varname [= value];  

稍微解释一下,看不懂没关系,耐心往下看你就会明白了:
varname 表示变量名,value 表示赋给变量的值,exp 表示一个表达式,方括号[ ]表示可有可无。

auto 和 decltype 都会自动推导出变量 varname 的类型,但是他们都推导方式有些区别:

  • auto 根据=右边的初始值 value 推导出变量的类型;所以auto
    要求变量必须初始化,也就是在定义变量的同时必须给它赋值,不然会报错的
  • decltype 根据 exp 表达式推导出变量的类型,跟=右边的 value 没有关系。 所以decltype 不要求初始化

闲话:
auto 将变量的类型和初始值绑定在一起,而 decltype 将变量的类型和初始值分开;虽然 auto 的书写更加简洁,但 decltype 的使用更加灵活。

先来一个简单的例子:

#include <iostream>
using namespace std;


int main()
{
    
	auto n1 = 5;//必须初始化,不然会报错,因为是根据初始值推导出变量的类型的

	decltype(10)n2;//没有初始化,可以不初始化,因为是根据()里面的内容推导出来的类型
	decltype(10)n3 = 100;//初始化了。

	auto str1 = "Gentle";
	decltype(str1) str2 = "QQ845264718";

	cout << "Hello world" << endl;
}

查看变量们的类型:
在这里插入图片描述

类型推导对对 cv 限定符的处理

「cv 限定符」是 const 和 volatile 关键字的统称:

  • const 关键字用来表示数据是只读的,也就是不能被修改;
  • volatile 和const 是相反的,它用来表示数据是可变的、易变的,目的是不让 CPU 将数据缓存到寄存器,而是从原始的内存中读取。

注意:
在推导变量类型时,auto 和 decltype 对 cv 限制符的处理是不一样的。decltype 会保留 cv 限定符,而 auto 有可能会去掉 cv 限定符。

auto 关键字对 cv 限定符的推导规则:

  • 如果表达式的类型不是指针或者引用,auto 会把 cv 限定符直接抛弃,推导成 non-const 或者 non-volatile 类型。
  • 如果表达式的类型是指针或者引用,auto 将保留 cv 限定符。

类型推导关键字对const限定符的推导例子:

#include <iostream>
using namespace std;


int main()
{
    
	//非指针非引用类型
	const int n1 = 100;

	auto n2 = 10;
	n2 = 99;

	decltype(n1) n3 = 20;
	//n3 = 5;  这里会报错:不能给常量赋值 表达式必须是可以修改的左值  证明:n3 是 const int 类型,跟n1类型一致。

	//指针类型   指向常量的指针 指针指向可以改,指针指向的值不可以改
	const int *p1 = &n1;

	auto p2 = p1;
	//*p2 = 66;   这里会报错:不能给常量赋值 表达式必须是可以修改的左值 证明p2 的类型和p1一样是 const int*。

	decltype(p1) p3;
	//*p3 = 19;  这里会报错:不能给常量赋值 表达式必须是可以修改的左值 证明p3 的类型和p1一样是 const int*。

	cout << "hello world" << endl;

}

对变量们类型的查看:
在这里插入图片描述
这里可以验证,类型推导对指针类型cv 限定符的推导规则。

类型推导关键字对引用推导

例子:

#include <iostream>
using namespace std;


int main()
{
    
	int n = 10;
	int &r1 = n;

	//auto推导
	auto r2 = r1;
	r2 = 20;
	cout << "n = " << n << ",  r1 = " << r1 << ", r2 = " << r2 << endl;

	//decltype推导
	decltype(r1)r3 = n;
	r3 = 99;
	cout << "n = " << n << ",  r1 = " << r1 << ", r3 = " << r2 << endl;

	cout << "hello world" << endl;

}

查看变量们的类型:
在这里插入图片描述
总结:
可以看出来auto把引用抛弃了,decltype则保留了引用。

闲谈:auto 虽然在书写格式上比 decltype 简单,但是它的推导规则复杂,有时候会改变表达式的原始类型;而 decltype 比较纯粹,它一般会坚持保留原始表达式的任何类型,让推导的结果更加原汁原味。

详细关于auto

我很早之前就写了一篇文章详细介绍了,请点连接。

decltype的具体用法

decltype的由来

既然已经有了 auto 关键字,为什么还需要 decltype 关键字呢?因为 auto 并不适用于所有的自动类型推导场景,在某些特殊情况下 auto 用起来非常不方便,甚至压根无法使用,所以 decltype 关键字也被引入到 C++11 中。

用法:

decltype(exp) varname; //不初始化
decltype(exp) varname = value; //初始化

其中,varname 表示变量名,value 表示赋给变量的值,exp 表示一个表达式。

exp 注意事项
原则上讲,exp 就是一个普通的表达式,它可以是任意复杂的形式(如函数),但是我们必须要保证 exp 的结果是有类型的,不能是 void;例如,当 exp 调用一个返回值类型为 void 的函数时,exp 的结果也是 void 类型,此时就会导致编译错误。

decltype 简单用法的例子:

#include <iostream>
using namespace std;


int main()
{
    
	int a = 0;
	decltype(a) b = 1;
	decltype(9.9) x = 1.1;
	decltype(x + 100) y;

	cout << "hello world" << endl;

}

然后查看一下变量们的类型
在这里插入图片描述

decltype 推导规则

decltype它的玩法实际上可以非常复杂。当程序员使用 decltype(exp) 获取类型时,编译器将根据以下三条规则得出结果:

  1. 如果 exp 是一个不被括号( )包围的表达式,或者是一个类成员访问表达式,或者是一个单独的变量,那么 decltype(exp) 的类型就和 exp 一致,这是最普遍最常见的情况。
  2. 如果 exp 是函数调用,那么 decltype(exp) 的类型就和函数返回值的类型一致。
  3. 如果 exp 是一个左值,或者被括号( )包围,那么 decltype(exp) 的类型就是 exp 的引用;假设 exp 的类型为 T,那么 decltype(exp) 的类型就是 T&。

exp为普通表达式的例子:

#include <iostream>

using namespace std;


class Student
{
    
public:
	static int total;
	string name;
	int age;
	float scores;

};

int main()
{
    
	int n = 0;
	const int &r = n;
	Student Stu;

	decltype(n) a = n; //n为int类型,a被推导为int类型
	decltype(r) b = n; //r为const int&类型,b被推导为const int&类型
	decltype(Stu) stu = Stu; //Stu为Student类型,stu被推导为Student类型
	decltype(Student::total) c = 0; //Student::total为Student的一个int类型变量,c被推导为int类型
	//Stu.name为Student的一个string类型,所以url被推导为string类型
	decltype(Stu.name) ur1 = "https://blog.csdn.net/weixin_50188452?spm=1001.2014.3001.5343";
	

}

查看变量们的类型:
在这里插入图片描述
对应推导规则 1,对于一般的表达式,decltype 的推导结果就和这个表达式的类型一致。

exp 为函数调用的例子:

#include <iostream>

using namespace std;

int func1(string, double);//返回值为int 类型
int& func2(int); //返回值为int& 类型
int&& func3(void); //返回值为int&&类型

const int func4(float); //返回值为const int 类型
const int& func5(int);//返回值为const int& 类型
const int&& func6(bool); //返回值为const int&& 类型


int main()
{
    
	int n = 10;
	decltype(func1("hello", 3.14)) a; //函数func1的返回值是int类型,所以a被推导为int类型
	decltype(func2(3)) b = n;	//函数func2的返回值是int&类型,所以b被推导为int&类型
	decltype(func3()) c = 0;//函数func3的返回值是int&&类型,所以c被推导为int&&类型


	decltype(func4(9.9)) d = 100;//函数func4的返回值是const int类型,d被推导为int类型(特例)
	decltype(func5(9)) e = d;//函数func5的返回值是const int&类型,所以e被推导为const int&类型
	decltype(func6(true)) f = 0;//函数func6的返回值是const int&&类型,所以f被推导为const int&&类型

	cout << "hello world" << endl;
}

查看变量的类型,发现了一个特例
在这里插入图片描述
注意:exp 中调用函数时需要带上括号和参数,但这仅仅是形式,并不会真的去执行函数代码。这里对应这规则2

exp 是左值,或者被( )包围的例子:

#include <iostream>
using namespace std;

class Base
{
    
public:
	int x;
};

int main()
{
    
	const Base obj;

	
	decltype(obj.x) a = 0; //不带括号,符合推导规则1,所以推导为int
	decltype((obj.x)) b = a;//带括号,符合推导规则3,所以推导为const int&

	int n = 5, m = 10;
	decltype(n + m) c = 0; //n+m得到右值,符合规则1,所以推导为int
	decltype(n = n + m) d = c; //n = n+m得到左值,符合推导规则3,所以推导为int&

	cout << "hello world" << endl;
}

查看一下变量们的类型:
在这里插入图片描述
这里对应着推导规则3。

补充左值和右值的理解:
左值是指那些在表达式执行结束后依然存在的数据,也就是持久性的数据;右值是指那些在表达式执行结束后不再存在的数据,也就是临时性的数据。有一种很简单的方法来区分左值和右值,对表达式取地址,如果编译器不报错就为左值,否则为右值。

decltype 的实际应用

应用于模板类型编程中的例子:

#include <iostream>
#include<vector>
using namespace std;

template <typename T>
class Base
{
    
public:
	void func(T& container)
	{
    
		m_it = container.begin();
	}
private:
	//typename T::iterator m_it;  //一起处理会报错
	//typename T::const_iterator m_it;  //在之前的 C++98/03 版本下只能想办法把 const 类型的容器用模板特化单独处理
	decltype(T().begin()) m_it; //C++11的做法,一起处理
};

int main()
{
    
	const vector<int> v;

	Base<const vector<int>> obj;

	obj.func(v);

	cout << "hello world" << endl;
	system("pause");
	return 0;
}

红色
红色

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_50188452/article/details/117125411

智能推荐

汽车小游戏简易制作_游戏小车程序-程序员宅基地

文章浏览阅读823次。跟着玉涛学长学做游戏一,首先要明确你要做什么游戏,所做的游戏要分哪几个类。我要做的是小汽车类的游戏,所以我要分以下几个类:小汽车一个类主体界面一个类按键监听也可以另作一个类二,创建一个文件夹 CarGame1.文件夹下创建一个包(package).2.先创建主体界面类(GameInterface)1.需要用到画笔和线程,还有类与对象的知识。首先 GameInter..._游戏小车程序

INS-40718 和 INS - 30516-程序员宅基地

文章浏览阅读1.1k次。RAC 安装的时候报错,INS-40718这个是自己填写的 scan name 和 /etc/hosts 里定义的不一致 可以cat/etc/hosts 看一下INS - 30516这个原因是 由于用使用原来安装的盘,没有清理过,所以报错清理一下就好dd if=/dev/zero of=/dev/raw/raw1 bs=1M..._ins30516

编程语言关于thinkphp邮箱验证码前后台_thinkphp6qq邮箱验证码-程序员宅基地

文章浏览阅读429次。编程语言关于thinkphp邮箱验证码前后台下面由thinkphp教程栏目给大家介绍关于thinkphp邮箱验证码前后台,希望对需要的朋友有所帮助!thinkphp邮箱验证码前后台思路:前台用jquery.validate验证,验证邮箱是否存在等等,交互用ajax来做。后端用邮箱类库直接上代码吧准备:1.下载PHPMailer类库,放到Application/Thinkphp/Library/Vendor/中,在网上搜搜,搜不到可以联系我2.开启SMTP,举个网易邮箱的例子_thinkphp6qq邮箱验证码

error C2660: 'wcscpy_s' : function does not take 2 arguments_error c2660 : function does not take 1 arguments-程序员宅基地

文章浏览阅读6.6k次。_tcscpy_s(m_item.sztype, ITEMTYPE_TEXT); tptextitem.cpp(119) : error C2660: 'wcscpy_s' : function does not take 2 arguments 原因选择的字符集不对。修改:Property -> C/C++ -> Language -> Treat wchar_t as Built-in Type 选择为 No (/Zc_error c2660 : function does not take 1 arguments

Ubuntu tftp服务器搭建过程-程序员宅基地

文章浏览阅读76次。1、安装相关软件包:Ubuntu tftp(服务端),tftp(客户端),xinetdroot@jiao:/# apt-get install tftpd tftp xinetd正在读取软件包列表... 完成正在分析软件包的依赖关系树正在读取状态信息... 完成下列【新】软件包将被安装: tftp tftpd xinetd升级了 0 ..._tftp please consult /usr/share/

ContextMenu结合SharedPreferences及ListView综合练习_如何通过listview和sharedpreference做一个计划列表-程序员宅基地

文章浏览阅读1k次。(一)、ContextMenu的用法:1、ContextMenuInfo 有什么用呢?当视图元素需要向上下文菜单传递一些信息,比如该View对应DB记录的id等,这就要使用ContextMenuInfo。需要传递额外信息的View需要重写getContextMenuInfo()方法,返回一个带有数据的ContextMenuInfo实现类对象。API中的官方解释:Additi_如何通过listview和sharedpreference做一个计划列表

随便推点

SpringMVC——-Controller返回格式化数据如JSON、XML的配置方式和机制_c# controller接收处理json、xml格式数据-程序员宅基地

文章浏览阅读7.1k次。1、本文内容我们在Web项目开发过程中,一般来说访问一个处理器,然后会返回一个视图,或者跳转到另外的处理器。但是随着项目越来越复杂,需求越来越复杂,对于处理器返回数据的类型要求也越来越多。比如要求能够返回JSON类型的数据、或者能够返回XML格式的数据,或者返回二进制的数据流等等。SpringMVC提供了这样的一个机制,允许控制器返回的数据不经过正常的视图处理流程,而是直接将返回的数据写入响应..._c# controller接收处理json、xml格式数据

如何使用libsvm进行分类(一)_libsvm中一对一分类代码-程序员宅基地

文章浏览阅读4k次。参考:点击打开链接环境为:matlab2014a+libsvm-3.20测试小例子:data = [176 70;180 80;161 45;163 47];label = [1;1;-1;-1];model = svmtrain(label,data);testdata = [190 85];testdatalabel = -1;[predictlabel,ac_libsvm中一对一分类代码

network-manager 查看设备显示WiFi “不可用” || 未发现 SSID 是“ wifi名称”的网络_nmcli 提示找不到ssid-程序员宅基地

文章浏览阅读5.9k次,点赞5次,收藏14次。最近想通过network-manager 来管理树莓派的无有线连接网络,在测试时出现 设备为WiFi状态为 不可用,但是卸载了network-manager, wifi可以正常连接。显示如下倒数第二行:最后发现是之前安装的wpasupplicant 和 network-manager 冲突了。network-manager 的使用需要依赖到wpasupplicant,可能..._nmcli 提示找不到ssid

嵌入式环境搭建之ssh_怎么用ssh进嵌入式系统-程序员宅基地

文章浏览阅读6k次,点赞2次,收藏3次。快毕业了。临走前帮导师搭建了gerrit,git服务器,其中涉及ssh的知识,就总结了下。希望对大家有帮助一、前言(ssh出世的原因)万物有因就有果,既然ssh存在,就必然有它存在的理由!许多网络程序,如telnet、rsh、rlogin或rexec,用明文(plain text)传送口令和秘密的信息,所以可利用任何连接到网络上的计算机监听这些程序和服务器之间的通信并获取口令和秘密信息。现在,te_怎么用ssh进嵌入式系统

O_CLOEXEC模式和FD_CLOEXEC选项_o_cloexec fd_cloexec-程序员宅基地

文章浏览阅读6.5k次,点赞3次,收藏7次。O_CLOEXEC模式和FD_CLOEXEC选项调用open函数O_CLOEXEC模式打开的文件描述符在执行exec调用新程序中关闭,且为原子操作。调用open函数不使用O_CLOEXEC模式打开的文件描述符,然后调用fcntl 函数设置FD_CLOEXEC选项,效果和使用O_CLOEXEC选项open函数相同,但分别调用open、fcnt两个函数,不是原子操作,多线程环境中存在竞态条件,故用o_o_cloexec fd_cloexec

python安徽合肥购物店铺数据可视化大屏全屏系统设计与实现(django框架)-程序员宅基地

文章浏览阅读2.1k次,点赞26次,收藏24次。python安徽合肥购物店铺数据可视化大屏全屏系统设计与实现(django框架): 本研究的思路是基于数据收集、数据处理、数据可视化设计和系统设计与实现的流程,通过采用 Python 的数据可视化库和 Django 框架进行开发,实现一个完整的大学生购物店铺数据可视化大屏全屏系统。以上为开题报告的完整内容,内容涵盖了研究背景与意义、国内外研究现状、研究思路与方法、研究内客和创新点、后台功能需求分析和前端功能需求分析、研究思路与研究方法、研究进度安排、论文(设计)写作提纲、主要参考文献等方面。

推荐文章

热门文章

相关标签