作用:返回表达式或变量的类型
返回值规则:
若e是一个左值(lvalue,即“可寻址值”),则decltype(e)将返回T& 若e是一个临终值(xvalue),则返回值为T&& 若e是一个纯右值(prvalue),则返回值为Tdecltype()不会执行括号内的表达式,decltype返回的类型是用于声明的,不能用于单纯的判断。比如decltype(a)==int,是不可以的,只能是在定义新的变量、返回值的地方使用:
int a=1; decltype(a) b (等价于int b)
若是给变量加多了1个括号,则会成为一个表达式。
int a = 1; int b=2; decltype((a)) d=b // decltype((a)) 返回类型int&。而引用必须赋初值,所以这里的d必须赋初值。
若表达式和指针相关的用法:
设p是指向int变量的指针
-
decltype(*p)-》返回int& 即引用
decltype(p)-》返回int* 即指针
decltype(&p)-》返回int** 即指针的指针。
这里解释一下为什么1返回的是引用而不是int:因为*p返回的本质上就是一个引用,当我们向*p赋值的时候,改变的是变量原本的值,而不是做了一个拷贝,显然这是引用的性质。
泛型编程中使用decltype:
通过和尾置返回类型结合,可以使得返回值可以由编译器推断,无需程序员指出。主要用于编写转发函数
int& foo(int& i); float foo(float& f); template <class T> auto transparent_forwarder(T& t) −> decltype(foo(t)) { return foo(t); }
像这个例子如果没有decltype,我们无法确定foo(t)到底是两个备选函数中的哪一个,因为这是在运行时决定的。这样子我们无法直接编写transparent_forwarder函数的返回值。
还有其他很多例子,decltype常用于难以确定变量类型的地方,而模板就是为了适配多类型而产生的,所以在泛型编程中,很多时候都会用到decltype来做到灵活定义变量类型。
类内初始化
C++11以前是不可以在一个类的数据成员声明的时候初始化的,除非是一个const的静态变量:
class A { static int i = 1; //correct,不得不在此赋值,因为const常量必须在声明时赋值 int num=2; //error,不允许在类内声明的时候对数据成员初始化 };
这样子带来繁琐的问题就是:尽管我们只是想为所有该类的实例的数据成员都设置一个初始值,也必须自己定义一个构造函数才能做到。
于是在c++11:允许直接在类内初始化值(前提:这个值必须是常量表达式)。
顺序:类内部初始化先于构造函数初始化进行,构造函数初始化会覆盖类内部初始化。也就是说,如果我们即定义了类内初始化值,又定义了自己的构造函数,最终的结果还是按照我们的意愿,对数据成员按照构造函数赋值。
使用方法:
class A { int num=2; //correct,C++11允许在类内声明的时候对数据成员初始化 int a{7} //用花括号赋值也可以,a=7 };
注意:C++11中,仍然没有改变静态数据成员必须在类内声明,类外初始化的事实。
class A { static int d = 1; //error }; int A::d = 1 //correct,一般来说:初始化语句会放在cpp文件,类定义放在h文件
列表初始化返回值
在C++11之前,如果我们想要返回一组数据,我们必须在子函数中构造一个对应的容器,借助容器来进行返回。
vector<int> process() { vector<int> v={1,2,3,4} return v; }
在新标准下,我们可以直接返回字面值,该字面值会用于容器的构造,而无需我们自己去构造。
vector<int> process() { return {1,2,3,4}; }
总结