【深入理解C++】decltype的推导规则、主要用途

1.decltype的推导规则

2.decltype的主要用途

2.1 decltype的应用多出现在泛型编程中

我们知道,迭代器一共有两种类型:只读const_iterator读写iterator。在下面代码中,类模板可以正确地处理非const容器,但遇到const容器时就会报错,原因就在于 ContainerType::iterator 并不能包括所有的迭代器类型。

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

template <typename ContainerType>
class myclass    // 类模板
{
public:
	typename ContainerType::iterator iter; // 不能包括所有的迭代器类型
	void myfunc(ContainerType& container)
	{
		// ...
		iter = container.begin();
		// ...
	}
};
int main()
{
	using VecType = std::vector<int>; // 使用using为非const容器定义别名
	VecType myvec = { 10, 20, 30, 40 }; // myvec是一个非const容器

	myclass<VecType> mc; // 类模板接收一个非const容器
	mc.myfunc(myvec); // 正确

	return 0;
}
int main()
{
	using VecType = const std::vector<int>; // 使用using为const容器定义别名
	VecType myvec = { 10, 20, 30, 40 }; // myvec是一个const容器

	myclass<VecType> mc; // 类模板接收一个const容器
	mc.myfunc(myvec); // 报错

	return 0;
}

此时,decltype 就可以完美地解决这个问题,如下代码所示,当 ContainerType 接收一个非const容器时,则会得到一个 读写iterator;当 ContainerType 接收一个const容器时,则会得到一个 只读const_iterator

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

template <typename ContainerType>
class myclass    // 类模板
{
public:
	decltype(ContainerType().begin()) iter; // 可以包括所有的迭代器类型
	void myfunc(ContainerType& container)
	{
		// ...
		iter = container.begin();
		// ...
	}
};
int main()
{
	using VecType = std::vector<int>; // 使用using为非const容器定义别名
	VecType myvec = { 10, 20, 30, 40 }; // myvec是一个非const容器

	myclass<VecType> mc; // 类模板接收一个非const容器
	mc.myfunc(myvec); // 正确

	return 0;
}
int main()
{
	using VecType = const std::vector<int>; // 使用using为const容器定义别名
	VecType myvec = { 10, 20, 30, 40 }; // myvec是一个const容器

	myclass<VecType> mc; // 类模板接收一个const容器
	mc.myfunc(myvec); // 正确

	return 0;
}

2.2 通过变量表达式抽取变量类型

在 C++11 的头文件中经常可以看到如下代码:

using size_t = decltype(sizeof(0));
using ptrdiff_t = decltype((int*)0 - (int*)0);
using nullptr_t = decltype(nullptr);

size_t、ptrdiff_t、nullptr_t 都是由 decltype 推导出来的类型,这种定义方法的好处是从类型的定义过程中就可以看出来这个类型的含义。

这种定义方式非常有意思,在一些常量、基本类型、运算符、操作符等都已经被定义好的情况下,类型可以按照规则被推导出来。而使用 using,就可以为这些类型取名,这样就颠覆了之前类型拓展需要将拓展类型“映射”到基本类型的常规做法。

2.3 auto结合decltype构成返回类型后置语法

#include <iostream>
using namespace std;

int fun(int& i)
{
	return i;
}

double fun(double& d)
{
	return d;
}

template <typename T>
auto FuncTemplate(T& t) -> decltype(fun(t)) // auto在这里没有自动类型推断的含义,它只是返回类型后置语法的组成部分
{
	return fun(t);
}

int main()
{
	int a = 180;
	cout << FuncTemplate(a) << endl; // 180

	double b = 179.9;
	cout << FuncTemplate(b) << endl; // 179.9

	return 0;
}

2.4 decltype(auto)

decltype(auto) 是 C++14 新增的类型指示符,可以用来声明变量以及指示函数返回类型。

2.4.1 用于函数返回类型

#include <iostream>
using namespace std;

template <typename T>
auto myfunc(T& val)    // auto推断出来是int类型
{
	val *= 2;
	return val;
}

int main()
{
	int a = 180;
	myfunc(a) = 120; // 报错
	cout << a << endl;

	return 0;
}
#include <iostream>
using namespace std;

template <typename T>
decltype(auto) myfunc(T& val)    // decltype(auto)推断出来是int&类型
{
	val *= 2;
	return val;
}

int main()
{
	int a = 180;
	myfunc(a) = 120; // 正确
	cout << a << endl;

	return 0;
}
#include <iostream>
using namespace std;

decltype(auto) fun1()
{
	int i = 110;
	return i; // 返回int类型
}

decltype(auto) fun2()
{
	int i = 120;
	return (i); // 返回int&类型
}

int main()
{
	fun1() = 10; // 报错
	fun2() = 20; // 语法上没问题,但会发生未预料行为,因为局部变量i的内存已经被系统回收了

	return 0;
}

2.4.2 用于变量声明中

#include <iostream>
using namespace std;

int main()
{
	const int& x = 180;
	auto y = x; // y为int类型,const和引用属性都没了
	decltype(auto) z = x; // z为const int&类型,auto丢掉的东西(const和引用)能够通过decltype(auto)捡回来

	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值