上一篇:《深入理解C++11》笔记-auto类型
本篇将介绍decltype的用法。decltype与auto类似,也能进行类型推导,但是用法有一定的区别,decltype推导出的类型能作为类型声明变量:
int main()
{
int i = 1;
decltype(i) j = 2;
std::cout << typeid(j).name() << std::endl; // int
getchar();
return 0;
}
decltype的应用
一种是decltype和typedef/using的合用:
using size_t = decltype(sizeof(0));
using ptrdiff_t = decltype((int*)0 - (int*)0);
using nullptr_t = decltype(nullptr);
另外,与auto相同可以用于简化代码增强可读性:
std::map<std::string, std::string> mapString;
using mapitor = decltype(mapString.begin());
for (mapitor i = mapString.begin(); i != mapString.end(); ++i)
{
}
for (decltype(mapString)::iterator i = mapString.begin(); i != mapString.end(); ++i)
{
}
还有一种用法是重用匿名类型,但是实际开发中匿名类型一般是为了不让外部进行重用,所以使用这种用法前需要多考虑:
struct {
int i = 1;
}anon;
int main()
{
decltype(anon) anon_struct;
anon_struct.i;
return 0;
}
还能扩大模板泛型的能力:
template<typename T1, typename T2>
void sum(const T1& a, const T2& b, decltype(a + b)& s) // 自动推导输出参数s的类型
{
s = a + b;
}
int main()
{
int a = 1;
long b = 2;
long s = 0;
sum(a, b, s); // 使用该模板需要提前推导出输出参数的类型,例如此处输出为long
return 0;
}
decltype推导四规则
int i = 0;
decltype(i) a;
decltpye((i)) b; // 编译失败,提示引用类型
如上代码,decltype双括号无法通过编译,因为decltype推导类型会有几个规则 - decltype(type):
- 如果type是一个没有带括号的标记符表达式(可以理解为变量名称)或类成员访问表达式,那么decltype(type)就是type对应的类型。此外,如果type是一个重载的函数,则会导致编译失败。
- 否则,假设type的类型为T,且是一个将亡值,那么decltype(type)为T&&。
- 否则,如果type是一个左值,那么decltype(type)为T&。
- 否则,decltype(type)为T。
再回头看之前的代码decltype(i) a;
使用了规则1,因为i是没有带括号的标记符表达式,所以推导的类型为int。而decltpye((i)) b;
是带括号的标记符表达式,所以使用了规则3,类型为int&。
我们再通过一些例子来详细了解一下以上规则的使用:
struct S { double d; } s;
int func(int);
int func(double);
int&& rvalue();
const bool isvalue(int);
int main()
{
int i = 0;
int arr[5] = { 0 };
int* ptr = arr;
decltype(arr) var1; // int[5],不带括号的标记符表达式
decltype(ptr) var2; // int*,不带括号的标记符表达式
decltype(s.d) var3; // double,不带括号的成员访问表达式
decltype(func) var4; // 编译失败,是重载的函数
decltype(rvalue()) var5 = 1; // int&&,将亡值
// 左值推导为类型的引用
decltype((i)) var6 = i; // int&带括号的左值
decltype(++i) var7= i; // int&,++i返回i的左值
decltype(arr[3]) var8 = i; // int&,[]返回i的左值
decltype(*ptr) var9 = i; // int&,*返回i的左值
decltype("lvalue") var10 = "lvalue"; // const char(&)[], 字符串字面常量为左值
// 以上都不是,推导为本类型
decltype(1) var11; // int,处理字符串字面常量以外的值为右值
decltype(i++) var14; // int, i++返回右值
decltype((isvalue(1))); // const bool,圆括号可以忽略
return 0;
}
cv限制符的继承与冗余的符号
上一篇中说到auto不能带走cv限制符(const、volatile),decltype是能够带走的。不过,如果对象定义中有const或volatile限制符,使用decltype推导时,其成员不会继承cv限制符。
int main()
{
int i = 0;
int& j = i;
decltype(i)& var1 = i; // int& 类型
decltype(j)& var2 = i; // int& 类型,推导出的类型已经有引用,冗余的引用会被忽略
int* ptr = &i;
//decltype(ptr)* var3 = &i; // 编译失败,int**裂隙不能指向int*类型
decltype(ptr)* var4 = &ptr; // int**类型,推导出的类型是指针,不会被忽略
const int k = 0;
decltype(k) var5 = 1; // const int类型
const decltype(k) var6 = 1; // const int类型,推导出的类型已经有const,冗余的const会被忽略
return 0;
}
可以看到,decltype推导的类型有时候会忽略一些冗余的符号,包括const、volatile、引用符号&。