constexpr
常量表达式是指不会改变并且在编译阶段就能得到计算结果的表达式.显然,字面值属于常量表达式,用常量表达式初始化的const
对象也是常量表达式.
一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定,例如:
const int max_file = 20; // max_files 是常量表达式
const int limit = max_files + 1; // limit 是常量表达式
int staff_size = 27; // staff_size 不是常量表达式
const int sz = get_size(); // sz 不是常量表达式
sz
虽然本身是一个常量,但它的具体值知道运行时才能获取,所以不是常量表达式.
constexpr
变量
判断一个初始值是不是常量表达式是很难的.
将变量声明为constexpr
类型以便于编译器来验证变量是否是一个常量表达式,声明为cosntexpr
的变量一定是一个常量,而且必须用常量表达式初始化:
constexpr int mf = 20; // 20 是常量表达式
constexpr int limit = mf + 1; // mf + 1 是常量表达式
constexpr int sz = size(); // 只有当 size 是一个 constexpr 函数时才正确
普通函数不能作为constexpr
变量的初始值, 只有constexpr
函数才可以.
字面值常量
字面值常量作为声明constexpr
用到的类型.
算术类型, 引用, 指针都属于字面值类型.
尽管指针和引用都能定义成constexpr
,但它们的初始值有严格限制,一个constexpr
指针的初始值必须是nullptr
或者0
,或者是存储于某个固定地址的对象.
指针和constexpr
在constexpr
声明中如果定义了一个指针,限定符constexpr
仅对指针有效,与指针所指的对象无关:
const int *p = nullptr; // p 是一个指向整型常量的指针
constexpr int *q = nullptr; // q 是一个指针整型的常量指针
p
与q
类型完全不同,constexpr
把q
定义为顶层const (int)*const q
.
constexpr
指针可以指向常量也可以指向非常量.
int j = 0;
constexpr int i = 42;
// i 与 j 必须定义在函数体外, 否则报错:
/*
constexpr.cpp:19:30: error: '& i' is not a constant expression
19 | constexpr const int *p = &i;
| ^~
constexpr.cpp:20:25: error: '& j' is not a constant expression
20 | constexpr int *p1 = &j;
*/
int main() {
constexpr int *np = nullptr;
constexpr const int *p = &i; // i 是 int 常量
constexpr int *p1 = &j; // j 是非常量
return 0;
}
由上面引出一个很有意思的问题:若i
的声明放在main
里面,仍然报错,因为i
虽然是一个常量了,但&i
不是,它的指针的地址不是固定的,那么用&i
去初始化constexpr
指针肯定也是错的.
int main() {
constexpr int i = 1;
cout << a[i] << endl;
return 0;
}
// 这里的用法是正确的, 这里不涉及&i, i是常量, 所以a[i]没有问题.
constexpr
函数
constexpr
函数是能用于常量表达式的函数,定义constexpr
函数的方法与其他函数类型,不过要满足几项要求:
- 函数的返回类型及所有形参的类型都是字面值类型.
- 函数有且只有一条
return
语句:
constexpr int new_sz() { return 1; }
int main() {
for (int i = 0; i < 5; ++i) a[i] = i;
constexpr int i = new_sz();
cout << a[i] << endl; // 1
cout << a[new_sz()] << endl; // 1
return 0;
}
执行该初始化任务时,编译器把对constexpr
函数的调用替换成结果值.为了能在编译过程中随时展开,constexpr
函数被隐式地指定为内联函数.
cosntexptr
函数体内也可以有其他语句,只要这些语句在运行时不执行任何操作就行.
我们允许constexpr
不返回常量.
constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }
如果实参是常量表达式,那么函数的返回值也是常量表达式,反之不然:
int arr[scale[2]]; // 正确
int i = 2;
int a2[scale[i]] // 错误 : scale[i] 不是常量表达式