constexpr 会在编译阶段求出来值,
在C++ 11 之前,const 关键字在实际使用中经常会表现出两种不同的语义:“只读” 和 “常量”,导致代码中有的地方语义不清,进一步导致编译时莫名报错。因此 C++ 11 新增关键字 constexpr,用来表示 “常量” 或 常量表达式,而 const 则仅仅表示 “只读”。目的是将运算尽量放在编译阶段,而不是运行阶段,另外,constexpr 还可以修饰函数、结构体。
1.修饰函数
- 函数体除了可以包含 using 指令、typedef 语句以及 static_assert 断言外,只能包含一条 return 返回语句。
- 函数只能引用全局不变常量。
- 函数只能调用其他 constexpr 修饰的函数。
- 函数必须有返回值(构造函数除外),即函数的返回值类型不能是 void,并且 prefix operation(v++)不允许出现。
// 通过对斐波拉契函数的递归实现(求斐波那契数列第n个的值),来看看constexpr具体怎么修饰函数,同时比较这样使用的好处。
// 由于fib1函数被constexpr修饰的,因此每一次计算的结果都会作为一个常量保存下来,复杂度等同于迭代的方法,基本上为O(n)。
constexpr long int fib1(int n)
{
return (n <= 1) ? n : fib1(n-1) + fib1(n-2);
}
// 熟悉递归函数就不难证明下面这个函数的时间复杂度为O(2^n)
long int fib2(int n)
{
return (n <= 1) ? n : fib2(n-1) + fib2(n-2);
}
// 定义如下数组时,需要明确使用constexpr来修饰的函数
std::array<int, fib1(10)> array1; // 编译ok
std::array<int, fib2(10)> array2; // 编译出错/*题外话:相信玩过王者荣耀的小伙伴,应该知道诸葛亮“黄金分割率”这款皮肤,台词“完美的几何学者,以斐波那契数列分割战场。”斐波那契数列:0,1,1,2,3,5,8,13,21,34,55,89,114...这个数列从第3项开始,每一项都等于前两项之和,如果设an为该数列的第n项,n属于N,那么这句话可以写成如下形式:an = an-1 + an-2。有趣的是,这样一个完全是自然数的数列,通项公式却是用无理数来表达的。而且当趋向于无穷大时,前一项与后一项的比值越来越逼近黄金分割0.618,所以它也被称为黄金数列!
*/
注意:1.常量表达式函数在调用位置之前必须要有该函数的实现,否则会编译报错。
2.constexpr 也可以修饰模板函数,但由于模板中类型的不确定性,因此模板函数实例化后的函数是否符合常量表达式函数的要求也是不确定的。针对这种情况下,C++11 标准规定,如果 constexpr 修饰的模板函数实例化结果不满足常量表达式函数的要求,则 constexpr 会被自动忽略,即该函数就等同于一个普通函数。
2.修饰类或结构体
对于自定义的数据类型(struct 或者 class ),直接用 constexpr 修饰是不行的,正确的方式是修饰其构造函数。
// C++ program to demonstrate uses of constexpr in constructor
#include <iostream>
using namespace std;
class Rectangle
{
int _h, _w;
public:
// 修饰一个类,函数体必须为空
constexpr Rectangle (int h, int w) : _h(h), _w(w) {}
// 修饰一个函数,_h, _w为全局,并且在实例化时就已经是初始化后的常量了
constexpr int getArea () { return _h * _w; }
};
int main()
{
// 对象在编译时就已经初始化了
constexpr Rectangle obj(10, 20);
cout << obj.getArea() << endl;
return 0;
}
注意,constexpr 修饰类的构造函数时,要求该构造函数的函数体必须为空,且采用初始化列表的方式为各个成员赋值时,必须使用常量表达式。