4.3 decltype

一、typeid与decltype

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总是以普通表达式为参数,返回推导的类型。

二、declytpe应用

与using和typedef结合使用,声明类型别名

比较常用的一种方式就是与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
}

可以推导模板的返回类型从而使用引用参数获取返回值,当然这里仍然需要声明实参,所以还是需要知道具体类型,用法比较鸡肋,牛逼的是后面的追踪返回类型。

只支持表达式,而支持函数名

三、decltype推导四规则

  • 如果是不带括号的标识符表达式或者类成员表达式,则按类型来,函数则会报错
  • 否则,e的类型是T,且e是将亡值,则返回类型是T&&
  • 否则,e的类型是T,e是一个左值,则返回类型是T&
  • 否则,类型是T

这里的否则应该是指带了括号。

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, 圆括号可以忽略

四、cv限制符与冗余的符号

cv限制符的继承

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,被忽略
}

你可能感兴趣的:(深入理解C++11新特性,C++11)