定义
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 能够根据变量、字面量、带有运算符表达式推导出变量类型
推导规则
-
若 参数exp 是一个不被括号( )包围表达式, 或是一个类成员访问表达式, 或者是一个单独变量, 则 decltype(exp)类型就和 exp 一致
-
若 exp 是函数调用, 则 decltype(exp)类型就和函数返回值类型一致
-
若 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;
};