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可变长数组和动态分配内存的区别:
- 生存期:变长数组的生存期在它最近的花括号内,动态数组在整个cpp程序
- 变长数组在栈上,动态分配在堆上。在栈上有缺点:不能太大,很容易导致栈溢出
- 变长数组说到底还是一旦确定了就不能改变长度(虽然他叫“变长”数组),不能重新修改数组长度。但是动态内存是可以在运行过程中重分配的,在运行过程中可长可短。
Reference
《C++ Primer 5》