美文网首页C++C++2.0
C++ decltype 与 auto关键字

C++ decltype 与 auto关键字

作者: wayyyy | 来源:发表于2018-07-05 23:29 被阅读5次
    顶层const 与 底层const

    指针本身是不是常量 和 指针所指的是不是一个常量 是2个问题。
    顶层const 表示指针本身是常量。
    底层const 表示指针所指的对象是个常量。
    更一般的是顶层const 表示任意对象是常量,底层const则是与指针和引用有关(使得被修饰的变量本身无法改变的const是顶层const,其他的通过指针或引用等间接途径来限制目标内容不可变的const是底层const)

    int i = 0;
    int *const p1 = &i;      // 不能改变p1的值(即p1只能指向i),这是一个顶层const
    
    const int ci = 42;       // 不能改变ci的值,这是一个顶层const
    const int *p2 = &ci;     // 不能改变ci的值,这是一个底层const
    
    const int* const p3 = p2;  // 前一个是底层const,后一个是顶层const
    const int &r = ci;   // 这是一个底层const
    

    当执行对象拷贝的操作时,底层const 和 顶层const区别明显。顶层const无所谓,但当对象是底层const时,拷入可拷出的对象必须具有相同的底层const资格。

    auto 关键字

    编程时常常需要把表达式的值赋给变量,这就要求在声明变量的时候清楚地知道表达式的类型,为了解决这个问题,C++11 引入auto类型说明符,用它能让编译器替我们去分析表达式所属的类型。auto让编译器通过初始值来推算变量的类型。显然auto定义的变量必须有初始值

    编译器推断出来的auto类型有时和初始值的类型并不完全一样,编译器会适当的改变结果类型使其更符合初始化规则。

    • 数组

      int arr[3] = {1, 2, 3};
      auto i = arr        // auto推断出的i是整型指针
      decltype(arr) arr2  // decltype 推断出的是和 arr 一样的数组类型
      
    • 引用类型
      使用引用其实是使用引用的对象,特别是当引用被用作初始值时,真正参与初始化的其实是引用对象的值,此时编译器以引用对象的类型作为auto的类型:

      int i = 0
      int &r = i;
      auto a = r;    // a是一个整数
      
    • auto 一般会忽略掉顶层const,同时底层const则会保留下来。

      const int ci = i;        // 顶层const
      const int &cr = ci;      // 底层const
      
      auto b = ci;     // b 是一个整数(ci 的顶层const特性被忽略掉了)
      auto c = cr;     // c 是一个整数 (cr 是引用)
      
      auto d = &i;     // d是int*
      auto e = &ci;    // e是const int*
      
      auto &g = ci;    // const int&
      
    decltype 关键字

    有时会遇到这种情况:希望从表达式的类型推断出要定义的变量类型,但是不想用该表达式的值初始化变量。为了满足这一要求,C++11标准引入了第二种类型说明符decltype,它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。

    decltype(f()) sum = x;  // sum的类型就是函数f的返回类型。
    

    decltype处理顶层const 和 引用的方式 与 auto 有些许不同,如果decltype使用表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内)

    const int ci = 0;
    const int &cj = ci;
    
    decltype(ci) x = 0;    // x 的类型是 const int    
    decltype(cj) y = x;    // y 的类型是const int&,y 绑定到变量x
    decltype(cj) z;        // 错误:z 是一个引用,必须初始化
    

    如果decltype使用的是表达式,则decltype返回表达式结果对应的类型。

    int i = 42;
    int *p = &i;
    int &r = i;
    
    decltype(r) j;         // j 是引用
    decltype(r+0) b;       // b是int
    decltype(*p) c;        // c是int&,这里必须要初始化
    

    r 是一个引用,则j也必须是引用。
    r+0 是一个算术表达式,返回的是一个右值,所以b是int。
    *p,解引用表达式返回的是左值,所以c是引用,而不是int。

    另外,变量名加上一对括号,则得到的类型也会与不加括号不同,如果 decltype 使用的是一个不加括号的变 量,则得到的结果就是该变量的类型。如果给变量加上了一层或多层括号,编译器就会把它当成一个表达式,变量是一种可以赋值语句左值的特殊表达式,所以这样的decltype就会得到引用类型。

    int i = 10;
    
    decltype(i) e;      // e是一个未初始化的int 
    decltype((i)) d;    // d是一个int&,这里必须要初始化
    
    常量表达式 与 constexpr 关键字

    常量表达式是指在编译期间就能计算得到值得表达式。
    字面值是常量表达式。
    用常量表达式初始化的const的对象也是常量表达式。

    一个对象是不是常量表达式,是由它的数据类型和初始值共同决定的,例如:

    const int a = 5;  // 常量表达式
    const int b = a+3;    // 常量表达式
    int c = 30;       // 非常量表达式,虽然初始值30是字面值,但是赋值操作需要在运行时才能进行
    const int sz = get_size();  // 非常量表达式,初始值不是常量,需要在运行时确定
    

    为了让编译器帮助验证常量表达式,C++11中引入了 constexpr关键字。如果想让一个表达式成为常量表达式,就使用 constexpr进行声明,当编译器发现声明的表达式并不是常量表达式时,就会报错,这就减少了出错的概率。

    #include <iostream>
    
    int func()  {  return 1;  }
    
    int main()  
    {
        constexpr int i = func();  // error,编译器不能计算得到Func的返回值。
    }
    

    constexpr 变量可以由特殊的constexpr函数初始化,C++11中对constexpr函数的形式有严格的要求:其函数体只能含有一条return 语句,并且return 语句只能含有字面值类型或其他的constexpr函数。

    #include <iostream>
    #include <string>
    
    int F1()  {  return 1;  }
    constexpr int F2(int n)  {  return 5;  }    // OK
    constexpr int F3(int n)  {  const expr int i = 5;  return 5;  }  // 
    constexpr int F4(int n)  {  return F1();  }  // 错误,返回非字面值类型
    

    参考资料
    1. Stanley B. Lippman, Barbara E. Moo. C++ primer[M]. 电子工业出版社,2013
    2. https://zhuanlan.zhihu.com/p/39307289

    相关文章

      网友评论

        本文标题:C++ decltype 与 auto关键字

        本文链接:https://www.haomeiwen.com/subject/eohuuftx.html