TheRiver | blog

You have reached the world's edge, none but devils play past here

0%

constexpr

参考

wiki

zhihu

正文

constexpr是C++11引入的关键字,用于编译时的常量与常量函数。

声明为constexpr函数的意义是:如果其参数均为合适的编译期常量,则对这个constexpr函数的调用就可用于期望常量表达式的场合(如模板的非类型参数,或枚举常量的值)。如果参数的值在运行期才能确定,或者虽然参数的值是编译期常量,但不匹配这个函数的要求,则对这个函数调用的求值只能在运行期进行。

C++编译时可确定常量表达式的结果,因此可在编译时优化。C++规范在一些地方要求使用常量表达式,如声明数组的维数。但常量表达式不允许包含函数调用或者对象构造。因此下述代码无效:

1
2
3
4
5
6
7
8
// error: array bound is not an integer constant before ‘]’ token
const int get_five() {return 5;}
int some_value[get_five() + 7];

int main()
{

}
1
2
3
4
5
6
7
8
//ok
constexpr int get_five() {return 5;}
int some_value[get_five() + 7];

int main()
{

}

constexpr的对象,必须是常量类型的,const则没要求(拷贝副本)。

1
2
3
4
5
6
7
8
int main()
{
int a = 100;
const int s = a; //ok
constexpr int ss = a; //error
constexpr int ss = 10; //ok

}

c++11 constexpr函数必须满足下述限制:

  • 函数返回值不能是void类型
  • 函数体不能声明变量或定义新的类型
  • 函数体只能包含声明、null语句或者一条return语句
  • 在形参实参结合后,return语句中的表达式为常量表达式

C++14放松了这些限制。声明为constexpr的函数可以含有以下内容:[2]

任何声明,除了:

  • static或thread_local变量。
  • 没有初始化的变量声明。
  • 条件分支语句if和switch。
  • 所有的循环语句,包括基于范围的for循环。
  • 表达式可以改变一个对象的值,只需该对象的生命期在声明为constexpr的函数内部开始。包括对有constexpr声明的任何非const非静态成员函数的调用。
  • goto仍然不允许在constexpr函数中出现。

constexpr支持编译期的递归。例如,可以写一个constexpr函数计算斐波那契数列。

*此外,C++11指出,所有被声明为constexpr的非静态成员函数也隐含声明为const(即函数不能修改this的值)(函数尾部的const,只读函数)。C++14已经删除此点,非静态成员函数可以为非const。**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class cc
{
public:
int _m = 0;
int func(int x = 0) const //不能修改*this
{
_m = 2; //error: assignment of member ‘cc::_m’ in read-only object

}

constexpr int func2(int x = 0)
{
return ++_m ? x : x; // error: increment of member ‘cc::_m’ in read-only object
}
};

总结

  • constexpr函数可以用在要求编译期常量的语境中。在这样的语境中,若你传给一个constexpr函数的实参值是在编译期已知的,则结果也会在编译期间计算出来。如果不能再编译期间计算得到,则编译不能通过
  • 在调用constexpr函数时,若传入的值有一个或多个在编译期未知,则它的运作方式和普通函数无异,亦即它也是在运行期执行结果的计算。这意味着,如果函数执行的是同样的操作,仅仅应用的语境一个是要求在编译期常量的,一个是用于所有其他值的话,那就不用写两个函数。

ending

2020071301.png

----------- ending -----------