C++11 decltype
【1】typeid与decltype
C++98对动态类型支持就是C++中的运行时类型识别(RTTI)。
(1)typeid
RTTI的机制是为每个类型产生一个type_info类型的数据,可以在程序中使用typeid随时查询一个变量的类型,typeid就会返回变量相应的type_info数据。
type_info的name成员函数可以返回类型的名字。
而在C++11中,又增加了hash_code这个成员函数,返回该类型唯一的哈希值,以供程序员对变量的类型随时进行比较。应用示例如下:
1 #include <iostream> 2 #include <typeinfo> 3 using namespace std; 4 5 class White {}; 6 class Black {}; 7 8 int main() 9 { 10 White a; 11 Black b; 12 cout << typeid(a).name() << endl; // class White 13 cout << typeid(b).name() << endl; // class Black 14 White c; 15 bool a_b_sametype = (typeid(a).hash_code() == typeid(b).hash_code()); 16 bool a_c_sametype = (typeid(a).hash_code() == typeid(c).hash_code()); 17 cout << "Same type? " << endl; 18 cout << "A and B? " << (int)a_b_sametype << endl; // 0 19 cout << "A and C? " << (int)a_c_sametype << endl; // 1 20 system("pause"); 21 } 22 23 /*运行结果 24 class White 25 class Black 26 Same type? 27 A and B? 0 28 A and C? 1 29 */
值得注意一点:相比于is_same模板函数的成员类型value在编译时得到信息,hash_code是运行时得到的信息。
(2)decltype
与auto类似地,decltype也能进行类型推导。下面分析两者的异同:
[1] decltype与auto的不同点
使用方式有一定的区别。示例如下:
1 #include <typeinfo> 2 #include <iostream> 3 using namespace std; 4 5 int main() 6 { 7 int i; 8 decltype(i) j = 0; 9 cout << typeid(j).name() << endl; // int 10 float a; 11 double b; 12 decltype(a + b) c; 13 cout << typeid(c).name() << endl; // double 14 } 15 16 /*运行结果 17 int 18 double 19 */
看到变量j的类型由decltype(i)进行声明,表示j的类型跟i相同(或者准确地说,跟i这个表达式返回的类型相同)。
而c的类型则跟(a+b)这个表达式返回的类型相同。由于a+b加法表达式返回的类型为double(a会被扩展为double类型与b相加),所以c的类型被decltype推导为double。
从这个例子中可以看到,decltype的类型推导并不是像auto一样是从变量声明的初始化表达式获得变量的类型,decltype总是以一个普通的表达式为参数,返回该表达式的类型。
[2] decltype与auto的相同点
2.1 作为一个类型指示符,decltype也可以将获得的类型来定义另外一个变量。
2.2 decltype类型推导也是在编译时进行的。
【2】decltype的应用
在C++11中,使用decltype推导类型是非常常见。
(1)decltype与typdef/using的合用。示例如下:
1 using size_t = decltype(sizeof(0)); 2 using ptrdiff_t = decltype((int*)0 - (int*)0); 3 using nullptr_t = decltype(nullptr);
(2)decltype在某些场景下,可以极大地增加代码的可读性。示例如下:
1 #include <vector> 2 using namespace std; 3 4 int main() 5 { 6 vector<int> vec; 7 typedef decltype(vec.begin()) vectype; 8 for (vectype i = vec.begin(); i < vec.end(); ++i) 9 { 10 // TODO... 11 } 12 for (decltype(vec)::iterator i = vec. begin(); i < vec. end(); ++i) 13 { 14 // TODO... 15 } 16 }
(3)重用匿名类型的利器。示例如下:
1 enum {K1, K2, K3 } anon_e; // 匿名的枚举 2 3 union 4 { 5 decltype(anon_e) key; 6 char* name; 7 } anon_u; // 匿名的union联合体 8 9 struct 10 { 11 int d; 12 decltype(anon_u) id; 13 } anon_s[100]; // 匿名的struct数组 14 15 int main() 16 { 17 decltype(anon_s) as; 18 as[0].id.key = decltype(anon_e)::K1; // 引用匿名类型枚举中的值 19 }
(4)扩大模板泛型的能力。示例如下:
1 template <typename T1, typename T2> 2 void Sum(T1& t1, T2& t2, decltype(t1 + t2)& s) 3 { 4 s = t1 + t2; 5 } 6 7 int main() 8 { 9 int a = 3; 10 long b = 5; 11 float c = 1.0f, d = 2.3f; 12 long e; 13 float f; 14 Sum(a, b, e); // s的类型被推导为long 15 Sum(c, d, f); // s的类型被推导为float 16 }
(5)实例化模板。示例如下:
1 #include <map> 2 using namespace std; 3 4 int getHash(char*); 5 6 map<char*, decltype(getHash)> dict_key; // 编译失败 7 map<char*, decltype(getHash(nullptr)) > dict_key1; // 编译成功 8 9 double getValue(int* p = nullptr); 10 map<int*, decltype(getValue())> dict_key3; // 编译成功
(6)在标准库中的一些应用。示例如下:
1 #include <type_traits> 2 using namespace std; 3 4 typedef double (*func)(); 5 6 int main() 7 { 8 result_of<func()>::type f; // 由func()推导其结果类型 9 }
【3】decltype推导规则
约定decltype(e)
(1)e没有带括号则推导原类型
[1] 写法decltype(e)
[2] 注意当e为表达式(函数)时,其函数不能重载,否则编译失败
(2)e带括号时推导规则
[1] 写法decltype((e))
[2] 推导如下:
若参数是任何其他类型为T的表达式,且
2.1 若e的值为将亡值,则decltype结果为T&&
2.2 若e的值为左值,则decltype结果为T&
2.3 若e的值为纯右值,则decltype结果为T
示例如下:
1 #include <iostream> 2 #include <typeinfo> 3 using namespace std; 4 5 class CTest 6 { 7 public: 8 int m_nNo; 9 }; 10 11 void overloadfunc(int) 12 {} 13 14 void overloadfunc(char) 15 {} 16 17 int&& rfvalue(); 18 19 const bool func() 20 { 21 return true; 22 } 23 24 int main() 25 { 26 int itest = 3; 27 int arr[3] = { 0 }; 28 int* pInt = arr; 29 30 // 不带括号 31 decltype(itest) ditest; // int 32 decltype(arr) darr; // int [3] 33 decltype(pInt) dpInt; // int * 34 35 CTest clTest; 36 decltype(clTest.m_nNo) dClTestNo; // int 37 38 //decltype(overloadfunc); // 编译不通过 39 40 // 带括号 规则1 将亡值 41 decltype(rfvalue()) r_value_f = 100; // 将亡值 int && 42 43 // 带括号 规则2 44 decltype(true ? itest : itest) ditest1 = itest; // int & 三目运算符,这里返回一个左值 45 46 decltype((itest)) ditest2 = itest; // int& 返回左值 47 48 decltype((++itest)) ditest3 = itest; // int& 返回左值 49 50 decltype(arr[1]) darr1 = itest; // int& []操作返回左值 51 52 decltype(*pInt) dpInt1 = itest; // int& *操作返回左值 53 54 decltype("hello") dstr = "world"; // const char(&)[6] 字符串字面常量为左值 55 56 // 带括号 规则3 57 decltype(12) dNum = 100; // int 58 decltype(itest++) dNum1 = 0; // int itest++返回右值 59 decltype(func()) dFunc; // const bool 推导为bool 60 }
自行仔细分析。
【4】cv限制符的继承与冗余的符号
(1)与auto不同点:auto类型推导时不能“带走”cv限制符不同。decltype是能够“带走”表达式的cv限制符的。
但是,如果对象的定义中有const或volatile限制符,使用decltype进行推导时,其成员变量不会继承const或volatile限制符。
示例如下:
1 #include <type_traits> 2 #include <iostream> 3 using namespace std; 4 5 const int ic = 0; 6 volatile int iv; 7 struct S { int i; }; 8 const S a = {0}; 9 volatile S b; 10 volatile S* p = &b; 11 const int func(int); 12 13 int main() 14 { 15 cout << is_const<decltype(ic)>::value << endl; // 1 16 cout << is_volatile<decltype(iv)>::value << endl; // 1 17 cout << is_const<decltype(a)>::value << endl; // 1 18 cout << is_volatile<decltype(b)>::value << endl; // 1 19 cout << is_const<decltype(a.i)>::value << endl; // 0, 成员变量i不是const 20 cout << is_volatile<decltype(p->i)>::value << endl; // 0, 成员变量i不是volatile 21 22 decltype(func(1)) bvar; 23 cout << is_const<decltype(func(1))>::value << endl; // 0 没有带走const 24 }
(2)与auto相同点:decltype从表达式推导出类型后,进行类型定义时,也会允许一些冗余的符号,比如cv限制符以及引用符号&。
但是,通常情况下,如果推导出的类型已经有了这些属性,冗余的符号则会被忽略。
示例如下:
1 #include <type_traits> 2 #include <iostream> 3 using namespace std; 4 5 int i = 1; 6 int& j = i; 7 int* p = &i; 8 const int k = 1; 9 10 int main() 11 { 12 decltype(i)& var1 = i; 13 decltype(j)& var2 = i; // 冗余的&, 被忽略 14 cout << is_lvalue_reference<decltype(var1)>::value << endl; // 1, 左值引用 15 cout << is_rvalue_reference<decltype(var2)>::value << endl; // 0, 不是右值引用 16 cout << is_lvalue_reference<decltype(var2)>::value << endl; // 1, 是左值引用 17 // decltype(p)* var3 = &i; // 无法通过编译 18 decltype(p)* var3 = &p; // var3的类型是int ** 19 auto* v3 = p; // v3的类型是int*(注意与decltype的区别) 20 v3 = &i; 21 }
good good study, day day up.
顺序 选择 循环 总结