关键字decltype和volatile简单说

一、引入关键字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 。
 
       如果需要多次声明,可结合使用typedefdecltype
         
       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 ,相当于告诉编译器,不要做上面的优化! 即每一次取变量的值,都要去变量所在的内存地址去读取!



 

你可能感兴趣的:(C++)