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