第一章 基础知识
1.6 常量
C++支持两种不变性概念:
- const:大致的意思是“我承诺不改变这个值”。主要用于说明接口,使得在用指针和引用将数据传递给函数时就不必担心数据会被改变了。编译器强行执行const做出的承诺。const的值可在运行时计算。
- constexpr:大致的意思是“在编译时求值”。主要用于说明常量,以允许将数据置于只读内存中(不太可能被破坏)以及提升性能。constexpr的值必须由编译器计算。
例如:
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声明的常量)。另一些情况下使用常量表达式是因为编译时求值对程序的性能非常重要。即使不考虑性能因素,不变性概念(对象状态不发生改变)也是一个重要的设计考量。