关键字 constexpr 于 C++11 中引入并于 C++14 中得到改善。它表示常数表达式。与 const 相同,它可应用于变量,因此如果任何代码试图修改该值,均将引发编译器错误。与 const 不同,constexpr 也可应用于函数和类构造函数。 constexpr 指示值或返回值是常数,并且如果可能,将在编译时计算值或返回值。
const 和 constexpr 变量之间的主要区别在于:const 变量的初始化可以延迟到运行时,而 constexpr 变量必须在编译时进行初始化。所有 constexpr 变量均为常量,因此必须使用常量表达式初始化。
constexpr float x = 42.0;
constexpr float y{108};
constexpr float z = exp(5, 3);
constexpr int i; // Error! Not initialized
int j = 0;
constexpr int k = j + 1; //Error! j not a constant expression
一般来说,如果你认定变量是一个常量表达式,那就把它声明成为constexpr类型。
constexpr 函数是在使用需要它的代码时,可以在编译时计算其返回值的函数。当其参数为 constexpr 值并且在编译时使用代码需要返回值时(例如,初始化一个 constexpr 变量或提供一个非类型模板参数),它会生成编译时常量。使用非constexpr 参数调用时,或编译时不需要其值时,它将与正则函数一样,在运行时生成一
#include
using namespace std;
// Pass by value
constexpr float exp(float x, int n)
{
return n == 0 ? 1 :
n % 2 == 0 ? exp(x * x, n / 2) :
exp(x * x, (n - 1) / 2) * x;
};
// Pass by reference
constexpr float exp2(const float& x, const int& n)
{
return n == 0 ? 1 :
n % 2 == 0 ? exp2(x * x, n / 2) :
exp2(x * x, (n - 1) / 2) * x;
};
// Compile time computation of array length
template<typename T, int N>
constexpr int length(const T(&ary)[N])
{
return N;
}
// Recursive constexpr function
constexpr int fac(int n)
{
return n == 1 ? 1 : n*fac(n - 1);
}
// User-defined type
class Foo
{
public:
constexpr explicit Foo(int i) : _i(i) {}
constexpr int GetValue()
{
return _i;
}
private:
int _i;
};
int main()
{
// foo is const:
constexpr Foo foo(5);
// foo = Foo(6); // Error!
//Compile time:
constexpr float x = exp(5, 3);
// const float a = 2.0;
// const int b = 2;
// constexpr float xx = exp2(a, b); // Error!
constexpr float xx = exp2(2.0, 2);
constexpr float y{ exp(2, 5) };
constexpr int val = foo.GetValue();
constexpr int f5 = fac(5);
const int nums[]{ 1, 2, 3, 4 };
const int nums2[length(nums) * 2]{ 1, 2, 3, 4, 5, 6, 7, 8 };
cout << "The value of foo is " << foo.GetValue() << endl;
}
3 constexpr和指针
还记得const与指针的规则吗?如果关键字const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针本身是常量;如果出现在星号两边,表示被指物和指针两者都是常量。
与const不同,在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指对象无关。
const int *p = 0; // non-const pointer, const data
constexpr int *q = 0; // const pointer, non-const data
与其它常量指针类似,const指针既可以指向常量也可以指向一个非常量:
int j = 0;
constexpr int i = 2;
constexpr const int *p = &i; // const pointer, const data
constexpr int *p1 = &j; // const pointer, non-const data
1:因为常量表达式在编译时就得到计算,故能声明为constexpr的类型必须是字面值类型,如算术类型,指针和引用;自定义类则不属于字面值类型。
2:对于声明为constexpr类型的指针,初始值必须为0或null,或这是某个固定地址;引用也类似
3:限定符constexpr只对指针有效,与其所指对象无关:constexpr int *q = nullptr;//q是一个指向整数的常量指针
4:可以用constexpr 函数 初始化constexpr变量,constexpr函数的声明定义方式如下:
constexpr int test () { return 1 ;}; constexpr int i = test ();
注意: 1,constexpr函数被隐式地指定为内联函数,在执行初始化时,编译器把对constexpr函数的调用替换成其结果值
2 ,constexpr函数可以返回非常量,所以, 如果 constexpr 包含参数,当该参数为非常量表达式时,其返回值也不是一个常量,例如
constexpr test ( int i ){ return i ; } ; int j = 2;
constexpr int k = test(j);//该 语句错误,返回值为非常量表达式