C++支持两种不变性概念:
例如:
constexpr int dmv = 17; //dmv是一个命名的常量 int var = 17; //var不是常量 const double sqv = sqrt(var); //sqv是一个命名常量,可能在运行时计算 double sum(const vector<double>&); //sum不会更改它的参数的值 vector<double>v{1.2, 3.4, 4.5}; //v不是常量 const double s1 = sum(v); //正确:sum(v)在运行时求值 constexpr double s2 = sum(v); //错误:sum(v)不是常量表达式
如果某个函数被用在常量表达式中(constant expression),即该表达式在编译时求值,则这个函数必须定义成constexpr。例如:
constexpr double square(double x){return x*x;} constexpr double max1 = 1.4*square(17); //正确,1.4*square(17)是常量表达式 constexpr double max2 = 1.4*square(var); //错误:var不是常量表达式 const double max3 = 1.4*square(var); //正确:可在运行时求值
constexpr函数可以接受非常量参数,但此时其结果不再是一个常量表达式。当程序的上下文不要求常量表达式时,我们可以使用非常量表达式参数来调用constexor函数,这样就不用将本来相同的函数定义两次了:一次用于常量表达式,另一次用于变量。
要想定义成constexpr,函数必须非常简单、无副作用且仅使用通过参数传递的信息。特别是,函数不能更改非局部变量,但可以包含循环以及使用自己的局部变量。例如:
constexpr double nth(double x, int n){ //假设0<=n double res = 1; int i = 0; while(i < n){ //while循环:当条件为真时继续循环 res *= x; ++i; } return res; }
在某些场合中,常量表达式是语言规则所要求的(如数组的界、case标签、模板值参数以及使用constexpr声明的常量)。另一些情况下使用常量表达式是因为编译时求值对程序的性能非常重要。即使不考虑性能因素,不变性概念(对象状态不发生改变)也是一个重要的设计考量。