C++11类型推导的关键字auto和decltype

他们的语法格式:

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;
}

红色
红色

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

困了就喝白茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值