一、引入关键字decltype
decltype是C++11标准新增的关键字,下面介绍这个关键字的主要用途:
问题描述:C++98标准中,编写模板函数时,一个问题是并非总能知道应在声明中使用哪种类型。下面示例:
template
void ft (T1 x, T2 y)
{
. . .
? xpy = x + y;
. . .
}
在这里,xpy应为什么类型呢?由于不知道 ft() 将如何使用,因此无法预先知道这一点。正确的类型可能是T1、T2或其他类型。
例如,T1可能是double,而T2可能是int, 在这种情况下,两个变量的和将为double类型。
T1可能是short,而T2可能是int,在这种情况下,两个变量和为int类型。
另外,结构和类可能重载运算符+,这将导致问题更加复杂。因此,在C++98标准中,没有办法声明xpy的类型。
那么,到底怎样声明xpy的类型呢?
C++11新增的关键字decltype提供了解决方案,我们可以这样使用这个关键字:
int x;
decltype(x) y;
上面语句是什么意思呢? 意思就是:使用 x 的类型来声明 y 变量。
更为重要的是,decltype的参数可以是表达式,因此在上面的模板中要声明xpy就可以这样干:
decltype ( x + y ) xpy;
xpy = x + y;
这样,就完美解决了一开始模板上出现的问题!^_^
二、深入理解使用decltype关键字
delcltype比上面的示例要复杂一点,为了确定类型,编译器必须遍历一个核对表。假设有如下声明:
decltype ( expression) var;
则核对表的简化版如下:
<1> 、第一步:如果expression是一个没有用括号括起的标识符,则var的类型与该标识符的类型相同,包括const等限定符:
double x = 5.5;
double y = 7.9;
double &rx = x;
const double * pd;
decltype (x) w; //w的类型和x一样,是double
decltype (rx) u = y; //u的类型和rx一样,是double&
decltype (pd) v; //v的类型和pd一样,是const double *
<2>、第二步:如果expression是一个函数调用,则var的类型与函数的返回值类型相同:
long indeed( int ); //一个参数为int,返回值类型为long的函数声明
decltype ( indeed( 3 ) ) m; //m的类型是long
注意:在这里,并不会实际调用函数。编译器通过查看函数原型来获悉返回值类型,而无需实际调用函数
<3>、第三步:如果expression是一个左值(变量),则var为指向其类型的引用。这好像意味着前面的 w 应为引用类型,因为 x 是一个左值。但别忘了,这种情况已经在第一步处理过了。 要进入第三步,expression不能是未用括号括起的标识符。那么expression什么时候将进入第三步呢?一种显而易见的情况是:expression是用括号括起的标识符。
double xx = 4.4;
decltype ( (xx) ) r2 = xx; //r2的类型是 double &
decltype ( xx ) w = xx; //这里 w 是 double 类型,因为它没有用括号括起 xx,所以不会进入第三步,在第一步处理
<4>、第四步:如果前面的条件都不满足,则var的类型与expression的类型相同:
int j = 3;
int &k = j;
int &n = j;
decltype ( j + 6 ) i1; //i1的类型是 int
decltype ( 100L ) i2; //i2的类型是 long
decltype ( k + n ) i3; //i3的类型是 int
注意:虽然 k 和 n 都是引用,但表达式 k + n 不是引用;它是两个int 的和,因此类型为 int 。
如果需要多次声明,可结合使用typedef 和 decltype:
typedef decltype ( x + y ) xytype;
xytype xpy = x + y;
三、后置返回类型
有一个相关的问题是decltype本身无法解决的,请看下面这个不完整的模板函数:
template
? Add ( T1 x, T2 y )
{
. . .
return x + y;
}
同样,无法预先知道将x 和 y 相加得到的类型。好像可以将返回值类型设置为delctype( x + y ),但不幸的是,此时还未声明参数 x 和 y,它们不在作用域内。必须在声明参数后使用decltype。为此C++新增了一种声明和定义函数的语法:
double fun (int x, float y);
使用新语法可写为:
auto fun ( int x , float y) -> double;
这将返回类型移到了参数声明后面。->double 被称为后置返回类型。其中auto是一个占位符,表示后置返回类型提供的类型,这时C++11给auto新增的一种角色。
通过结合使用这种语法和decltype,便可以给 Add 函数模板指定返回值类型,如下所示:
template
auto Add (T1 x, T2 y ) -> decltype ( x + y )
{
. . .
return x + y;
}
现在,decltype在参数声明后面,因此 x 和 y 位于作用域内,可以使用它们。
四、 volatile关键字
volatile 是cv - 限定符中的 v,c表示const。const关键字我们很清楚它的用途。它表明,内存被初始化后,程序便不能再对它进行修改。那volatile是干什么的呢?
关键字volatile表明,即使程序代码没有对内存单元进行修改,其值也可能发生变化。
听起来似乎很玄乎,实际上并非如此。例如,可以将一个指针指向某个硬件位置,其中包含了来自串行端口的时间或信息。这种情况下,硬件可能修改其中的内容,使其发生变化,而程序未对其进行操作。
而编译器有时会做一些优化工作:
例如,假设编译器发现,程序在几条语句中两次使用了某个变量的值,则编译器可能不是让程序去该变量所在内存地址查找这个值两次,而是将这个值缓存到寄存器中。这种优化假设变量的值在这两次使用之间不会变化。如果不将变量声明为volatile,则编译器将执行这种优化。
而此时,如果这个变量的值可以通过硬件来改变,那么这种优化可能导致所取到的变量值并不是最新的,将可能导致程序错误!
将变量声明为 volatile ,相当于告诉编译器,不要做上面的优化!
即每一次取变量的值,都要去变量所在的内存地址去读取!