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.

顺序 选择 循环 总结

posted @ 2020-01-30 23:18  kaizenly  阅读(604)  评论(0编辑  收藏  举报
打赏