C++ auto和decltype

来源:互联网 发布:公司招人用什么软件 编辑:程序博客网 时间:2024/05/26 02:18

C++ auto和decltype

    C++11新标准引入了auto和decltype这两种类型说明符。它们的共同之处在于不需要显式声明变量的类型,而是让编译器去推断变量类型。但是,它们之间又有一些细微的差异。下面就让我们一起深入理解这两个类型说明符。


auto

    因为auto是根据表达式的类型来推断变量的类型,所以auto定义的变量必须要有初始值,如:

auto val1 = 2;              //val1为int型auto val2 = 'a';            //val2为char型auto val3 = val1 + val2;    //val2隐式转换为int型,所以val3为int型auto *val4 = &val3;         //val4为int*型

    以上的例子似乎让我们认为,auto确定的类型和初始值的类型完全一样。其实不是的,让我们看一些其他的例子。

当auto遇上引用时:

int i = 0;                  //i为int型变量const int &i1 = i;          //i1为对非常量的常量引用const int j = 0;            //j为int型常量const int &j1 = j;          //j1为对常量的常量引用auto val5 = j;              //val5为int型auto val6 = i1;             //val6为int型auto val7 = j1;             //val7为int型

    首先我们需要理解常量引用这个概念,其实严格来讲,并不存在常量引用。因为引用不是一个对象,所以不可能让引用本身恒定不变。那么,我们为什么要使用“常量引用”这个说法呢,其实更多的是为了表达这种引用关系,即通过该引用不能改变被引用的对象。了解这个,也就理解了上述的“对非常量的常量引用”和“对常量的常量引用”。为了使文字不过于使人迷惑,我们之后都称呼“被引用对象”为原始对象。

引用图

    接下来,因为引用又可以当做被引用对象的别名。所以无论引用是常量引用(有const)还是非常量引用(无const),最后都会演变为通过推断原始对象的类型来定义变量:即编译器以原始对象的类型作为auto类型

  • auto val6 = i1; —–(变成)—> auto val6 = i;很好理解val6为int型。

  • auto val7 = j1; —–(变成)—> auto val7 = j;,所以val7和val5类型一样。

    关于val5和val7的类型,就不得不说auto的一个很重要的性质,就是当设置auto类型的变量(非引用),auto会忽略初始值的顶层const,保留底层const

  • 对于非指针变量(非引用)而言,只可能存在顶层const。当变量为常量,则该变量存在顶层const;若变量为非常量,则该变量不存在顶层const。

  • 对于引用而言,该引用为常量引用时,存在顶层const;反之不存在。指针引用则有可能出现底层const。

  • 对于指针的顶层/底层const之后会详细介绍。

    因为j为常量,存在顶层const,所以auto忽略j的顶层const,所以val7和val5均为int型。如果想要使用auto定义常量对象,则要显示声明,const auto val8 = j;这样val8就是int型常量。

    另外当设置auto类型的引用时,auto会保留初始值的顶层const

    当auto &val9 = i1;时,此时i1的顶层const属性被保留,所以val9为常量引用。其实也很好理解,如果对于一个常量引用x,之后绑定在x上的引用x1,x2…如果不是常量引用,那不就能通过x1,x2等来改变x引用的对象,这显然是不合理的,C++也不会允许这样的漏洞。

当auto遇上指针时:

    首先我们需要理解之前提到的顶层const底层const的概念,对于指针而言:

  • 顶层const表示指针本身是个常量。
  • 底层const表示指针所指的对象是个常量。
const int i = 20;           //i为整型常量const int *p1 = &i;         //p1为指向常量的指针,具有底层constconst int *const p2 = &i;   //p2为指向常量的常量指针,具有顶层const和底层const

顶层/底层const

    我们直接来看代码:

auto p3 = p1;               //保留底层const,所以p3类型为const int*auto p4 = p2;               //保留底层const,忽略顶层const,所以p4类型为const int*auto p5 = &i;               //&i为const int*型,所以保留底层const,p5类型为const int*

    相信根据上述的保留底层const,忽略顶层const性质,大家都能很好的理解。


decltype

    如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。相对于auto的不完全一致,decltype则是完全对应与表达式结果的类型。

    这种一致性体现在,如果表达式的结果对象能作为一条赋值语句的左值,那么这个表达式将向decltype返回一个引用类型。

int i = 42, *p = &i, &r = i;decltype(r + 0) v1;         //r + 0的结果为int型,所以v1为int型decltype(*p) v2 = i;        //*p为左值,所以v2为int&decltype(r) v3 = i;         //rint&,为左值,所以v3为int&

    要注意的是:decltype对于变量是否加括号有着不同的处理:

decltype(i) v4;             //i不加括号,v4为i的类型,即intdecltype((i)) v5 = i;       //i加括号,将其当成表达式,变量是一种可以可以作为左值的特殊表达式,所以v5为int&

总结

auto总结

    关于auto类型的引用和指针,再多说一些。由于引用和指针都与初始值相关。可以想象,初始值绑定引用并衍生指针。所以如果初始值是常量,那么后续的引用和指针都应该是绑定常量或指向常量。
decltype总结

原创粉丝点击