重用匿名类型
以下代码使用3种不同的匿名类型,decltype可以推导其类型并且进行重用。
enum class{K1, K2, K3}anon_e; // 匿名的强类型枚举
union {
decltype(anon_e) key;
char* name;
}anon_u; // 匿名的union
struct {
int d;
decltype(anon_u) id;
}anon_s[100]; // 匿名的struct数组
int main() {
decltype(anon_s) as;
as[0].id.key = decltype(anon_e)::K1; // 引用匿名强类型枚举中的值
}
扩大模板泛型的能力
// s的类型被声明为decltype(t1 + t2)
template<typename T1, typename T2>
void Sum(T1 & t1, T2 & t2, decltype(t1 + t2) & s) {
s = t1 + t2;
}
int main() {
int a = 3;
long b = 5;
float c = 1.0f, d = 2.3f;
long e;
float f;
Sum(a, b, e); // s的类型被推导为long
Sum(c, d, f); // s的类型被推导为float
}
实例化模板
int hash(char*);
//map<char*, decltype(hash)> dict_key; // 无法通过编译
map<char*, decltype(hash(nullptr))> dict_key;
注意,decltype只能接受表达式做参数,像函数名做参数的表达式decltype(hash)是无法通过编译的。
举例使用decltype的标准库
基于decltype的模板类result_of,其作用是推导函数的返回类型。
typedef double (*func)();
int main() {
result_of<func()>::type f; // 由func()推导其结果类型
}
这里的f类型最终被推导为double,而result_of并没有真正调用func()这个函数,这一切都是因为底层的实现使用了decltype。
result_of的一个可能实现方式如下:
template<class>
struct result_of;
template<class F, class... ArgTypes>
struct result_of<F(ArgTypes...)>
{
typedef decltype(
std::decltype<F>()(std::decltype<ArgTypes>()...)
) type;
};
推导四规则
当使用decltype(e)语句来获取类型时,编译器将依序判断以下四规则:
- 如果e是一个没有带括号的标记符表达式(id-expression)或者类成员访问表达式,那么decltype(e)就是e所命名的实体的类型。此外,如果e是一个被重载的函数,则会导致编译时错误。
- 否则,假设e的类型是T,如果e是一个将亡值(xvalue),那么decltype(e)为T&&。
- 否则,假设e的类型是T,如果e是一个左值,则decltype(e)为T&。
- 否则,假设e的类型是T,则decltype(e)为T。
所有出去关键字、字面量等编译器要使用的标记之外的自定义的标记(token)都可以是标记符(identifier)。
int i = 4;
int arr[5] = {0};
int *ptr = arr;
struct S { double d; } s;
void Overloaded(int);
void Overloaded(char); // 重载的函数
int && RvalRef();
const bool Func(int);
// 规则1: 单个标记符表达式以及访问类成员,推导为本类型
decltype(arr) var1; // int[5], 标记符表达式
decltype(ptr) var2; // int*, 标记符表达式
decltype(s.d) var4; // double, 成员访问表达式
decltype(Overloaded) var5; // 无法通过编译,是个重载的函数
// 规则2: 将亡值,推导为类型的右值引用
decltype(RvalRef()) var6 = 1; // int&&
// 规则3: 左值,推导为类型的引用
decltype(true ? i : i) var7 = i; // int&, 三元运算符,这里返回一个i的左值
decltype((i)) var8 = i; // int&, 带圆括号的左值
decltype(++i) var9 = i; // int&, ++i返回i的左值
decltype(arr[3]) var10 = i; // int& []操作返回左值
decltype(*ptr) var11 = i; // int& *操作返回左值
decltype("lval") var12 = "lval"; // const char(&)[5], 字符串字面常量为左值
// 规则4:以上都不是,推导为本类型
decltype(1) var13; // int, 除字符串外字面常量为右值
decltype(i++) var14; // int, i++返回右值
decltype((Func(1))) var15; // const bool, 圆括号可以忽略
{
int j = 3;
int k& = j;
int n& = j;
decltype(k+n) var16; // int, k+n 返回int
}
```