引子
decltype类型推导还挺复杂,有时候会出一些奇怪的错误,比如下面的程序编译不过,因为括号里的i推导成引用了。
int main() {
int i;
decltype(i) a;
decltype((i)) b;
}
// cpp.cpp:5:19: error: ‘b’ declared as reference but not initialized
四规则
当程序员用decltype(e)获得类型,
- 如果e是一个没有带括号的标记符表达式(id-expression)或者类表达式,那么decltype(e)就是e所命名的实体的类型。此外,如果e是一个被重载的函数,则会导致编译时错误。
- 否则,假设e的类型是T,如果e是一个将亡值(xvalue),那么decltype(e)为T&&。
- 否则,假设e的类型是T,如果e是一个左值,则decltype(e)为T&。
- 否则,假设e的类型是T,则decltype(e)为T。
标记符表达式就是常规理解的表达式,比如int arr[4];,arr是标记符表达式,但arr[3]就不是,已经对arr进行了操作。
引子里的例子,a属于规则1,b属于规则3,(i)带了括号,又实实在在有地址、属于左值,因此落到了规则3。
四规则示例
int main() {
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; // error: decltype cannot resolve address of overloaded function
// 规则2
decltype(RvalRef()) var6 = 1; // xrvalue, int&&
// 规则3
decltype(true? i : i) var7 = i; // lvalue int&
decltype((i)) var8 = i; // int&
decltype(++i) var9 = i; // int&
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
decltype((Func(1))) var15; // const bool
}
上面的程序覆盖了四种规则。
关于var5,把void Overloaded(char);那行删掉就能编译通过了,没有重载的函数是可以放在decltype里的。
另外值得注意的是i++和++i那句,i++得到的是int,++i得到的是int&。这是因为++i返回的是操作后的结果,类似(i),不再是标记符表达式,有地址跟i一样,落在了规则3;i++不是i,不是规则1,但也没有地址,落在了规则4。
打印结果。
#include <type_traits>
#include <iostream>
using namespace std;
int i = 4;
int arr[5] = {};
int *ptr = arr;
int && RvalRef();
int main(){
cout << is_rvalue_reference<decltype(RvalRef())>::value << endl; // 1
cout << is_lvalue_reference<decltype(true? i : i)>::value << endl; // 1
cout << is_lvalue_reference<decltype((i))>::value << endl; // 1
cout << is_lvalue_reference<decltype(++i)>::value << endl; // 1
cout << is_lvalue_reference<decltype(arr[3])>::value << endl; // 1
cout << is_lvalue_reference<decltype(*ptr)>::value << endl; // 1
cout << is_lvalue_reference<decltype("lval")>::value << endl; // 1
cout << is_lvalue_reference<decltype(i++)>::value << endl; // 0,不是左值引用也不是右值引用,就是左值
cout << is_rvalue_reference<decltype(i++)>::value << endl; // 0,改成is_integral就能出来1了
}