C++ decltype 与 auto 关键字

顶层const 与 底层const

指针本身是不是常量 和 指针所指的是不是一个常量 是2个问题:

  • 顶层const 表示指针本身是常量。
  • 底层const 表示指针所指的对象是个常量。

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

// cons在*左边,指物是常量(底层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&,这里必须要初始化

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

你可能感兴趣的:(C++ decltype 与 auto 关键字)