一、constexpr的介绍
constexpr是C++11开始提出的关键字,功能是使指定的常量表达式获得在程序编译阶段计算出结果的能力,而不必等到程序运行阶段。以定义数组为例,数组的长度就必须是一个常量表达式。
注:所谓常量表达式,指的就是由多个常量组成的表达式。
如下代码,我们可以看到
// 1)
int url[10]; //正确
// 2)
int url[6 + 4]; //正确
// 3)
int length = 6;
int url[length]; //错误,length是变量
二、constexpr使用
constexpr所修饰的变量一定是编译期可求值的
constexpr int foo( int i)
{
return i + 5;
}
int main()
{
constexpr int a = foo(1); // ok
constexpr int b = foo(cin.get()); // !error
constexpr int c = a * 2 + 1; // ok
}
以下编译失败,对于funcTest,胆小的编译器并没有足够的胆量去做编译期优化,哪怕函数体就一句return 字面值
const int funcTest() {
return 10;
}
int main()
{
int arr[funcTest()];
return 0;
}
//error : 函数调用在常量表达式中必须具有常量值
修改后,编译通过,编译期大胆地将funcTest()做了优化,在编译期就确定了func计算出的值10而无需等到运行时再去计算。
constexpr int funcTest() {
return 10;
}
int main()
{
int arr[funcTest()];
}
三、与const的比较
我们发现,const 关键字在实际使用中经常会表现出两种不同的语义。举个例子:
#include <iostream>
#include <array>
using namespace std;
void dis_1(const int x){
//错误,x是只读的变量
array <int,x> myarr{1,2,3,4,5};
cout << myarr[1] << endl;
}
void dis_2(){
const int x = 5;
array <int,x> myarr{1,2,3,4,5};
cout << myarr[1] << endl;
}
int main()
{
dis_1(5);
dis_2();
return 0;
}
可以看到,dis_1() 和 dis_2() 函数中都包含一个 const int x,但 dis_1() 函数中的 x 无法完成初始化 array 容器的任务,而 dis_2() 函数中的 x 却可以。
这是因为,dis_1() 函数中的“const int x”只是想强调 x 是一个只读的变量,其本质仍为变量,无法用来初始化 array 容器;而 dis_2() 函数中的“const int x”,表明 x 是一个只读变量的同时,x 还是一个值为 5 的常量,所以可以用来初始化 array 容器。
有读者可能会问,“只读”不就意味着其不能被修改吗?答案是否定的,“只读”和“不允许被修改”之间并没有必然的联系,举个例子:
#include <iostream>
using namespace std;
int main()
{
int a = 10;
const int & con_b = a;
cout << con_b << endl;
a = 20;
cout << con_b << endl;
return 0;
}
可以看到,程序中用 const 修饰了 con_b 变量,表示该变量“只读”,即无法通过变量自身去修改自己的值。但这并不意味着 con_b 的值不能借助其它变量间接改变,通过改变 a 的值就可以使 con_b 的值发生变化。
为了解决 const 关键字的双重语义问题,保留了 const 表示“只读”的语义,而将“常量”的语义划分给了新添加的 constexpr 关键字。因此 C++11 标准中,建议将 const 和 constexpr 的功能区分开,即凡是表达“只读”语义的场景都使用 const,表达“常量”语义的场景都使用 constexpr。
在大部分实际场景中,const 和 constexpr 是可以混用的,例如:
const int a = 5 + 4;
constexpr int a = 5 + 4;
四、总结
- constexpr是一种很强的约束,更好地保证程序的正确语义不被破坏。
- 编译器可以在编译期对constexpr的代码进行非常大的优化,比如将用到的constexpr表达式都直接替换成最终结果等。
- constexpr相比宏来说,没有额外的开销,但更安全可靠。
参考: