C++11 类型推导decltype

重用匿名类型

以下代码使用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
	}
	```
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章