天天看点

C++11中的constexpr

一、为什么要用constexpr

有时我们需要在程序编译时就能得到某个对象或表达式的值,例如定义数组时指定数组大小的值。我们把这种表达式称为常量表达式。

字面值属于常亮表达式,用常亮表达式初始化的const对象也是常量表达式。所以我们说一个对象(或表达式)是不是常亮表达式由它的数据类型和初始值共同决定。这里举两个反例:

string i="hello";                       //初始值是常量表达式但数据类型不是常量类型

const int j=get_size();               //数据类型是常量类型但初始值不是常量表达式

但是在一个复杂系统中,很难(几乎肯定不可能)分辨一个初始值到底是不是常量表达式。其次如果要定义一个常量表达式对象,即使我们让它的数据类型为const的,也不能完全保证这个对象就是常量表达式。

所以迫切需要一种解决此问题的方法。

二、constexpr的使用规则

1、C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化,否则编译器将报错。

constexpr int i=20;                          //20是常量表达式

constexpr int j=i+1;                         //i+1是常量表达式

constexpr int k=size();                    //当size是个constexpr函数时,才是正确的声明语句

2、常量表达式的值需要在编译时就得到计算,因此对声明constexpr时用到的类型必须有所限制。因为这些类型一般比较简单,值也显而易见、容易得到,就把它们称为“字面值类型”。

   目前接触过的类型中,算数类型、引用和指针和某些类都属于字面值类型。自定义类、IO库、string类型则不属于字面值类型,也就不能被定义成constexpr。

3、尽管指针和引用都能定义成constexpr,但是它们的初始值却受到严格限制。一个constexpr指针必须是nullptr或0,或者是存储于某个固定地址中的对象。

   函数体内定义的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量。定义于所有函数体之外的对象或者局部的静态变量其地址固定不变,能用来初始化constexpr指针。

4、constexpr声明中如果定义了一个指针,限定符constexpr仅仅对指针有效,与指针所指的对象无关。constexpr把它所定义的对象置为了顶层const。

const int *p=nullptr;                 //p是指针常亮

int *const r=nullptr;                 //r是常亮指针

constexpr int *q=nullptr;          //q是常亮指针

5、constexpr函数

   5.1、constexpr函数是指能用于常量表达式的函数。定义constexpr函数的方法与其它函数类似,不过要遵守几项约定:函数的返回类型及所有形参的类型都是字面值类型,而且函数体中必须有且只有一条return语句。

constexpr int i(){return 0;}

constexpr int foo=i();

   5.2、当把constexpr函数用于初始化操作时,编译器把对constexpr函数的调用替换成其结果值。为了能在编译过程中随时展开,constexpr函数被隐式地指定为内联函数,所以可以在程序中多次定义,且多次定义必须完全一致,故常定义在头文件中。

   5.3、constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作即可。例如空语句、类型别名以及using声明。

   5.4、允许constexpr函数返回非常量值,有时它的返回值可以依赖于函数实参,即实参是常量表达式时,返回值也是常量表达式;反之,当实参不是常量表达式时,其返回值也就不是常量表达式。

   虽然constexpr函数允许返回非常量表达式的值,但是当把该函数用在需要常量表达式的上下文中时,编译器会检查函数结果是否符合要求,若不符合将提示错误。

6、字面值常量类中的constexpr构造函数

参见——构造函数第六部分

继续阅读