C++11特性总结(3)-constexpr怎么用?

1,847 阅读3分钟

constexpr

用法

即可修饰变量又可以是函数。

限制

对于变量

要求其赋值必须是常量表达式,即在编译期间由编译器计算结果并赋值。因为是常量所以不能在声明后再赋值,必须是在声明的同时初始化,否则会编译错误。

对于函数

如果其传入的参数可以在编译时期计算出来,那么这个函数就会产生编译时期的值,在函数被调用的地方替换成这个值。但是,如果传入的参数如果不能在编译时期计算出来,那么constexpr修饰的函数就和普通函数一样。只能在运行时确定结果。不过,这样子并不会有什么问题产生,我们不必因此而写两个版本,所以如果函数体适用于constexpr函数的条件,可以尽量加上constexpr。

好处

把运行时的工作部分计算工作交给了编译器来解决,提高了运行时的效率。

//变量的使用
constexpr int a=3;    //接下来使用a的时候,a就是const int类型
const int* num =&a;   //所以当声明一个a的指针的时候,也要用const int
constexpr int b;   //Error:default initialization,不能缺省初始化值
-----------------------------------------------------------------------------
//函数的使用
constexpr int addNum(int a,int b)  
{
    return a+b;
}

int main()
{
    int a,b;
    cin>>a>>b;  //这样子使用,在编译器无法得到其值,因为a、b是运行时确定的数值
    cout<<addNum(a,b)<<endl;  //正常输出,没有问题
    int c=3,d=4;    //c、d编译时期已经有值
    cout<<addNum(c,d)<<endl;  //编译时期就计算出了addNum(c,d)的值
}

const 和constexpr的区别

共同点

两者都必须在声明的同时赋值,即初始化。

不同点

constexpr:必须初始化为一个能确定地址的变量/常量值,比如全局变量、static局部变量。(这两者都处于静态存储区,属于静态分配:在程序编译期间分配固定的存储空间)

const:没有限制一定要明确地址。所以const必须赋值,但不一定要是确定的值。

int n;
cin>>n;

constexpr int i=1; //正确
constexpr int i=n; //error,在编译期间,n不是一个确定的值

const int j=n;  //正确
const int j; //error,必须初始化

实际上,const只是规定了不能改变值,即read-only,const常用于不需要改变值的访问。constexpr才是真正英文里的"const",即常量的意思。

const可能在编译期得出结果,也可能在运行期。但是constexpr的变量一定要在编译期得到结果。所以constexpr可以用来定义数组大小,但是const不可以。(这里就涉及到一个问题,数组大小也是一定要在编译期确定吗?

数组大小一定要在编译期确定吗

这里有一个反例,历史上C99标准定义了可变长数组(Variable Length Size-VLA),所以在C/C++编程中,经常会有以下这种写法:

int n;
cin>>n;
int num[n];  //定义一个可变长度的数组

以此标准定义的数组大小确实是在运行时才确定的,既然如此,为什么我们还要用new/malloc来分配内存呢

VLA可变长数组和动态分配内存的区别:

  1. 生存期:变长数组的生存期在它最近的花括号内,动态数组在整个cpp程序
  2. 变长数组在栈上,动态分配在堆上。在栈上有缺点:不能太大,很容易导致栈溢出
  3. 变长数组说到底还是一旦确定了就不能改变长度(虽然他叫“变长”数组),不能重新修改数组长度。但是动态内存是可以在运行过程中重分配的,在运行过程中可长可短。

Reference

《C++ Primer 5》