文章目录
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;
}