重用匿名類型
以下代碼使用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
}
```