C++C++2.0

C++ decltype 与 auto关键字

2018-07-05  本文已影响5人  wayyyy
顶层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类型有时和初始值的类型并不完全一样,编译器会适当的改变结果类型使其更符合初始化规则。

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

上一篇下一篇

猜你喜欢

热点阅读