decltype

 

定义

C++11新增关键字decltype(declare type)声明类型, 功能是在编译时期进行自动类型推导

与auto区别

auto 和 decltype 关键字都可以自动推导出变量类型

auto varName = value;

decltype(exp) varName = value;

varName 表示变量名, value 表示值, exp 表示表达式

auto 根据value 推导出变量类型, 要求变量必须初始化

decltype 根据 exp 表达式推导出变量类型, 跟 value 没有关系, 不要求变量必须初始化

exp 注意事项

exp 就是一个普通表达式, 可以是任意复杂形式, 但是必须要保证结果有类型, 不能是 void

当 exp 调用一个返回值类型为 void 函数时, exp结果也是 void 类型, 会导致编译错误

int a = 0;

// b 被推导成了 int
decltype(a) b = 1;

// x 被推导成了 double
decltype(10.8) x = 5.5;

// y 被推导成了 double
decltype(x + 100) y;

decltype 能够根据变量、字面量、带有运算符表达式推导出变量类型

推导规则

  1. 若 参数exp 是一个不被括号( )包围表达式, 或是一个类成员访问表达式, 或者是一个单独变量, 则 decltype(exp)类型就和 exp 一致

  2. 若 exp 是函数调用, 则 decltype(exp)类型就和函数返回值类型一致

  3. 若 exp 是一个左值, 或者被括号( )包围, 则 decltype(exp) 类型就是 exp 的引用

假设 exp 类型为 T, 那么 decltype(exp) 类型就是 T&

#include <iostream>
#include <string>

class Student {
public:
    static int total;

    int age;
    float scores;
    std::string name;
};

int Student::total = 0;

int main() {
    int n = 0;

    const int &r = n;

    Student stu;

    // n 为 int 类型, a 被推导为 int 类型
    decltype(n) a = n;

    // r 为 const int& 类型, b 被推导为 const int& 类型
    decltype(r) b = n;

    // total 为类 Student 一个 int 类型成员变量, c 被推导为 int 类型
    decltype(Student::total) c = 0;

    // total 为类 Student一个 string 类型成员变量,  url 被推导为 string 类型
    decltype(stu.name) url = "http://666.com";

    return 0;
}

按照推导规则 1, 对于一般表达式, decltype 推导结果和这个表达式类型一致

函数调用

// 函数声明

// 返回值为 int&
int& func_int_r(int, char);

// 返回值为 int&&
int&& func_int_rr(void);

// 返回值为 int
int func_int(double);

// 返回值为 const int&
const int& fun_cint_r(int, int, int);

// 返回值为 const int&&
const int&& func_cint_rr(void);

// decltype类型推导
int n = 100;

// a类型为 int&
decltype(func_int_r(100, 'A')) a = n;

// b类型为 int&&
decltype(func_int_rr()) b = 0;

// c类型为 int
decltype(func_int(10.5)) c = 0;

// x类型为 const int &
decltype(fun_cint_r(1,2,3)) x = n;

// y类型为 const int&&
decltype(func_cint_rr()) y = 0;

参数exp中调用函数时需要带上括号和参数, 但这仅仅是形式, 并不会真去执行函数代码

参数是左值或被()包围

#include <iostream>

class Base {
public:
    int x;
};

int main() {
    const Base obj;
    // 带有括号表达式

    // obj.x 为类成员访问表达式, 符合推导规则一, a 类型为 int
    decltype(obj.x) a = 0;

    // obj.x 带有括号, 符合推导规则三, b类型为 int&
    decltype((obj.x)) b = a;

    // 加法表达式
    int n = 0, m = 0;

    // n+m 得到一个右值, 符合推导规则一, 所以推导结果为 int
    decltype(n + m) c = 0;

    // n=n+m 得到一个左值, 符号推导规则三, 所以推导结果为 int&
    decltype(n = n + m) d = c;

    return 0;
}

左值是指那些在表达式执行结束后依然存在数据, 也就是持久性数据

右值是指那些在表达式执行结束后不再存在数据, 也就是临时性数据

对表达式取地址, 若编译器不报错就为左值, 否则为右值

应用

auto 只能用于类静态成员, 不能用于类非静态成员(普通成员)

若想推导非静态成员类型, 就必须使用 decltype

#include <iostream>
#include <vector>

template <typename T>
class Base{
public:
    void Func(T& container) {
        mIt = container.begin();
    }
private:
    T::iterator mIt;
};

int main() {
    const std::vector<int> v;
    Base<const std::vector<int>> obj;
    obj.Func(v);
    return 0;
}

使用 Base 类时若传入一个 const 类型容器, 编译器会报错

原因在于T::iterator并不能包括所有迭代器类型, 当 T 是一个 const 容器时应当使用 const_iterator

有decltype, 可改为

template <typename T>
class Base {
public:
    void Func(T& container) {
        mIt = container.begin();
    }
private:
    decltype(T().begin()) mIt;
};