decltype的使用
引入关键字decltype的原因是有时候我们仅仅希望使用表达式的类型而不使用它的值,可以通过decltype,编译器分析表达式并得到它的类型,但并不实际计算表达式的值。
基础
double f();
int main() {
int temp = 0;
decltype(temp) a = 0; //a的类型为int
decltype(f()) b = 1.0; //b的类型为double,尽管函数f()并没有定义
//编译器并不实际调用函数,只是分析
}
decltype与const、引用
decltype和auto处理顶层const以及处理引用的方式不同,如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用)
int i = 0, &j = i;
decltype(j) h = i; //h的类型是int&
const int ci = 0, &cj = ci;
decltype(ci) x = 0; //x的类型是const int
decltype(cj) y = x; //y的类型是const int&,y必须绑定到int或者const int变量上
int *const pi = &i;
const int *const cpi = &ci;
decltype(pi) dpi = &i; //dpi的类型为int *const
decltype(cpi) dcpi = &ci; //dcpi的类型为const int *const
骚操作
decltype的结果类型与表达式形式密切相关,对于decltype所用的表达式来说,如果变量名加上了一对括号,则得到的类型与不加括号时会有所不同,如果decltype使用的是一个不加括号的变量,则得到的结果就是该变量的类型;如果给变量加上一层或多层括号,编译器就会把它当成是一个表达式。变量是一种可以作为赋值语句左值的特殊表达式,这样的decltype就会得到引用类型。
**切记:**decltype((variable))(注意是双层括号)得到的结果永远是引用,而decltype(variable)只有当variable本身是引用时才得到引用。
int i = 42, j = 0;
decltype(i) e; //e的类型为int(未初始化)
decltype((i)) d = j; //d的类型为int&,必须初始化
上述来自《C++ Primer》第五版,我不是很理解,看了C++decltype((i))为什么是引用所说的,稍微理解
- decltype((i)) --> (i)
- decltype(i) --> i
第一个decltype处理的是(i),第二个处理的是i
在C++中,第一个是“lvalue表达式”,第二个是“一个变量”
因为是“lvalue表达式”,decltype会理解为引用。
这篇博客对decltype推导规则的总结很到位
decltype推导四规则
- 如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么的decltype(e)就是e所命名的实体的类型。此外,如果e是一个被重载的函数,则会导致编译错误
- 否则 ,假设e的类型是T,如果e是一个将亡值,那么decltype(e)为T&&
- 否则,假设e的类型是T,如果e是一个左值,那么decltype(e)为T&
- 则,假设e的类型是T,则decltype(e)为T
标记符指的是除去关键字、字面量等编译器需要使用的标记之外的程序员自己定义的标记,而单个标记符对应的表达式即为标记符表达式。具体案例如下
int i = 4;
double dd = 0.0;
int arr[5] = { 0 };
int *ptr = arr;
struct S{ double d;}s ;
void Overloaded(int);
void Overloaded(char);//重载的函数
int && RvalRef();
const bool Func(int);
//规则一:推导为其类型
decltype (arr) var1; //int[5] 标记符表达式
decltype (ptr) var2;//int* 标记符表达式
decltype(s.d) var3;//double 成员访问表达式
//decltype(Overloaded) var4;//重载函数。编译错误。
//规则二:将亡值。推导为类型的右值引用。
decltype (RvalRef()) var5 = 1;
//规则三:左值,推导为类型的引用。
decltype ((i)) var6 = i; //int&
decltype (true ? i : i) var7 = i; //int& 条件表达式返回左值。
decltype (++i) var8 = i; //int& ++i返回i的左值,注意这里并不会真的执行++i
decltype(arr[5]) var9 = i;//int&. []操作返回左值
decltype(*ptr)var10 = i;//int& *操作返回左值
decltype("hello") var11 = "hello"; //const char(&)[6] 字符串字面常量为左值,且为const左值。
//字符串字面值常量是个左值,且是const左值,而非字符串字面值常量则是个右值。
//规则四:以上都不是,则推导为本类型
decltype(1) var12;//const int
decltype(Func(1)) var13=true;//const bool
decltype(i++) var14 = i;//int i++返回右值 注意这里并不会真的执行i++
C++标准库<type_traits>提供三个模板类,可以判断是否是左值引用还是右值引用、引用类型。is_rvalue_reference,is_lvalue_reference,is_reference
使用方法:cout << std::is_lvalue_reference<decltype(“hello”)>::value << endl;
输出为1。
我认为作为简单的使用可以按如下推导,decltype使用的包括变量、返回左值的表达式、返回右值的表达式,使用变量时,按照变量的原本类型推导,包括引用和const属性,使用返回左值的表达式时,推导为左值表达式类型的引用;使用右值表达式时,推导为右值的类型。