c++98支持RTTI(run time type Identification),RTTI就是为每个类型产生一个type_info类型的数据,并提供接口type_id(x)获取type_info.
而在C++11中,又增加了hash_code这个成员函数,返回该类型唯一的哈希值,以供程序员对变量的类型随时进行比较。
#include
#include
using namespace std;
class White{};
class Black{};
int main() {
White a;
Black b;
cout << typeid(a).name() << endl; // 5White
cout << typeid(b).name() << endl; // 5Black
White c;
bool a_b_sametype = (typeid(a).hash_code() == typeid(b).hash_code());
bool a_c_sametype = (typeid(a).hash_code() == typeid(c).hash_code());
cout << "Same type? " << endl;
cout << "A and B? " << (int)a_b_sametype << endl; // 0
cout << "A and C? " << (int)a_c_sametype << endl; // 1
}
C++11推出的decltype总是以普通表达式为参数,返回推导的类型。
比较常用的一种方式就是与typdef/using的合用,从而声明类型别名。
using size_t = decltype(sizeof(0));
using ptrdiff_t = decltype((int*)0- (int*)0);
using nullptr_t = decltype(nullptr);
通过类型别名,我们可以简化类型名,提高代码可读性:
#include
using namespace std;
int main() {
vector vec;
typedef decltype(vec.begin()) vectype;
for (vectype i = vec.begin(); i < vec.end(); i++) {
// 做一些事情
}
for (decltype(vec)::iterator i = vec.begin(); i < vec.end(); i++) {
// 做一些事情
}
}
有时,我们声明的临时类型并没有赋予名称,那么我们将无法二次使用这些类型,但是有了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
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 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(&)[9], 字符串字面常量为左值
// 规则4:以上都不是,推导为本类型
decltype(1) var13; // int, 除字符串外字面常量为右值
decltype(i++) var14; // int, i++返回右值
decltype((Func(1))) var15; // const bool, 圆括号可以忽略
decltype能够继承cv限制符、引用和指针,但是只是针对声明本身,对于对象如果是cv的,其成员类型于成员本身是否cv有关(下例的a.i)
#include
#include
using namespace std;
const int ic = 0;
volatile int iv;
struct S { int i; };
const S a = {0};
volatile S b;
volatile S* p = &b;
int main() {
cout << is_const::value << endl; // 1
cout << is_volatile::value << endl; // 1
cout << is_const::value << endl; // 1
cout << is_volatile::value << endl; // 1
cout << is_const::value << endl; // 0, 成员不是const
cout << is_volatilei)>::value << endl; // 0, 成员不是volatile
}
// 编译选项:g++ -std=c++11 4-3-12.cpp
对于cv以及&,如果推导的类型已经有了,而又指定了,则通常会忽略decltype()后的cv和&。
但是需要注意的是*,auto会忽略,而decltype不会忽略,而是叠加。
#include
#include
using namespace std;
int i = 1;
int & j = i;
int * p = &i;
const int k = 1;
int main() {
decltype(i) & var1 = i;
decltype(j) & var2 = i; // 冗余的&, 被忽略
cout << is_lvalue_reference::value << endl;// 1, 是左值引用
cout << is_rvalue_reference::value << endl;// 0, 不是右值引用
cout << is_lvalue_reference::value << endl;// 1, 是左值引用
decltype(p)* var3 = &i; // 无法通过编译
decltype(p)* var3 = &p; // var3的类型是int**
auto* v3 = p; // v3的类型是int*
v3 = &i;
const decltype(k) var4 = 1; // 冗余的const,被忽略
}