3.18 constexpr函数
constexpr函数是C++11及以后标准的核心特性,核心作用是让函数能在「编译阶段」计算出结果(而非运行阶段),从而提升程序性能、增强代码安全性,还能用于要求"编译期常量"的场景。
一、先搞懂核心概念:编译期计算 vs 运行期计算
- 普通函数 :调用时在程序「运行阶段」计算结果(比如运行时才执行
add(3,5)得到8); - constexpr函数 :满足条件时,编译器会在「编译阶段」直接算出结果(比如编译时就把
add(3,5)替换成8),运行时无需计算。
可以把constexpr函数想象成"编译器能执行的函数"------只要输入是编译期常量,结果就提前算好,运行时直接用结果,省掉计算开销。
二、constexpr函数的核心用途(按重要性排序)
- 提升程序性能:编译期计算,避免运行时开销
对于需要反复调用、逻辑简单的函数(比如数学计算、常量推导),constexpr能把计算提前到编译阶段,运行时直接用结果,尤其适合高频调用的场景。
示例:普通函数 vs constexpr函数
cpp
#include <iostream>
using namespace std;
// 普通函数:运行时计算
int add(int a, int b) {
return a + b;
}
// constexpr函数:编译期计算(输入为常量时)
constexpr int constexpr_add(int a, int b) {
return a + b;
}
int main() {
// 1. 普通函数:运行时计算3+5
int res1 = add(3, 5);
// 2. constexpr函数:编译时就算出8,运行时直接赋值
constexpr int res2 = constexpr_add(3, 5);
cout << res1 << " " << res2 << endl; // 输出8 8
return 0;
}
- 反编译能看到:
res2的赋值直接是8,没有函数调用指令;res1则有函数调用的指令。
- 用于"编译期常量场景"(必须用编译期值的地方)
C++中有些场景要求"值必须是编译期常量"(比如数组大小、模板参数、枚举值),普通函数的返回值无法满足,但constexpr函数可以。
示例:用constexpr函数定义数组大小
cpp
#include <iostream>
using namespace std;
// 计算数组大小的constexpr函数
constexpr int get_arr_size(int n) {
return n * 2;
}
int main() {
// 1. 错误:普通函数的返回值是运行时的,不能定义数组大小
// int size = add(3,2);
// int arr1[size]; // 编译报错(非编译期常量)
// 2. 正确:constexpr函数返回编译期常量,可定义数组大小
constexpr int arr_size = get_arr_size(3); // 编译期算出6
int arr2[arr_size] = {1,2,3,4,5,6}; // 合法,数组大小是6
cout << "数组大小:" << sizeof(arr2)/sizeof(int) << endl; // 输出6
return 0;
}
- 增强代码安全性:编译期检查错误
如果constexpr函数的输入是常量,但逻辑有问题(比如除以0),编译器会直接报错,而不是运行时崩溃,能提前发现问题。
示例:编译期检查错误
cpp
constexpr int div(int a, int b) {
return a / b; // 除法逻辑
}
int main() {
// 编译时报错:division by zero(除以0)
// constexpr int res = div(10, 0);
return 0;
}
- 普通函数调用
div(10,0)会在运行时崩溃,而constexpr函数直接在编译期暴露问题。
- 兼容运行时调用:灵活度高
constexpr函数并非"只能编译期执行"------如果输入是运行时变量(比如用户输入的值),它会退化成普通函数,在运行时计算,不影响使用。
示例:兼容运行时调用
cpp
constexpr int mul(int a, int b) {
return a * b;
}
int main() {
int x, y;
cin >> x >> y; // 运行时输入(变量)
// 此时mul退化成普通函数,运行时计算x*y
int res = mul(x, y);
cout << res << endl;
return 0;
}
三、constexpr函数的使用条件(关键限制)
要让constexpr函数能在编译期计算,必须满足:
- 函数体足够简单(C++11只允许1条return语句;C++14及以后支持循环、分支等);
- 输入参数必须是编译期常量(否则退化成运行时计算);
- 返回值必须能在编译期确定。
总结
| 核心用途 | 具体价值 |
|---|---|
| 编译期计算 | 提升性能,避免运行时重复计算 |
| 支持编译期常量场景 | 可用于数组大小、模板参数等要求"编译期值"的地方 |
| 编译期错误检查 | 提前发现逻辑错误(如除以0),增强代码安全性 |
| 兼容运行时调用 | 输入是变量时退化成普通函数,灵活度不损失 |
简单来说,constexpr函数的核心是"把能提前算的都在编译期算完"------既提升性能,又保证安全,是C++中优化常量计算的核心工具。
3.18.1 constexpr与const的区别
constexpr和const的核心区别------这两个关键词都和"不可修改"有关,但作用场景、生效时机完全不同:
一、核心区别总览
| 对比维度 | const |
constexpr |
|---|---|---|
| 核心语义 | 「只读」:变量/对象不能被修改 | 「编译期常量」:值在编译期确定 |
| 生效时机 | 运行期(修饰变量)/编译期(修饰常量) | 编译期(函数/变量均在编译期确定) |
| 适用对象 | 变量、函数参数、类成员等 | 函数、变量、表达式等 |
| 能否参与编译期计算 | 仅const常量(如const int a=5)可以,const变量(如const int a=x)不行 |
只要满足条件,必然参与编译期计算 |
二、分场景详细对比
- 修饰变量:
const变量 vsconstexpr变量
cpp
#include <iostream>
using namespace std;
int main() {
int x = 10; // 运行时变量
// const变量:
const int a = 5; // 「编译期常量」:值是字面量,不可修改
const int b = x; // 「运行时只读变量」:值是运行时变量,不可修改,但编译期不确定
// constexpr变量:
constexpr int c = 5; // 「编译期常量」:值必须在编译期确定
// constexpr int d = x; // 编译报错:x是运行时变量,无法在编译期确定值
// 关键差异:能否用于「编译期常量场景」
int arr1[a]; // 合法(a是const编译期常量)
// int arr2[b]; // 编译报错(b是const运行时变量,无法确定数组大小)
int arr3[c]; // 合法(c是constexpr编译期常量)
return 0;
}
const变量分两种:若初始化值是字面量/编译期常量 ,则是编译期常量;若初始化值是运行时变量,则是"运行时只读变量"(无法参与编译期计算);constexpr变量必须是编译期常量,初始化值必须能在编译期确定,否则直接报错。
- 修饰函数:
const函数 vsconstexpr函数
cpp
// const函数(仅用于类成员函数):
class A {
public:
int val = 5;
// const成员函数:保证函数内不修改类成员变量(运行期约束)
int get_val() const {
// val = 10; // 编译报错:const函数不能修改成员变量
return val;
}
};
// constexpr函数:
// 编译期计算的函数(输入是编译期常量时,结果在编译期确定)
constexpr int add(int a, int b) {
return a + b;
}
int main() {
A obj;
cout << obj.get_val() << endl; // 运行时调用const成员函数
constexpr int res = add(3,5); // 编译期计算出8
cout << res << endl;
return 0;
}
const函数仅用于类成员函数,作用是"保证函数内不修改类成员"(运行期的只读约束);constexpr函数是"编译期可执行的函数",核心是计算时机提前,和"是否修改数据"无关。
- 参与编译期计算的能力
cpp
// const的局限性:
const int m = 5; // 编译期常量,可参与编译期计算
const int n = m + 3; // 编译期计算出8(m是编译期常量)
int y = 2;
const int p = y + 3; // 运行时变量(y是运行时输入),无法参与编译期计算
// constexpr的优势:
constexpr int q = 5;
constexpr int r = q + 3; // 编译期计算出8
constexpr int s = add(2,3); // 调用constexpr函数,编译期计算出5
const只有"初始化值是编译期常量"时,才能参与编译期计算;constexpr只要满足条件,必然在编译期计算,没有例外。
三、一句话总结区别
const是"运行期的只读保护":告诉你"这个东西不能改",但不一定知道它的值是多少;constexpr是"编译期的确定值":告诉你"这个东西的值在编译期就定了,不仅不能改,还能提前用"。
简单来说:
- 若只想"禁止修改",用
const; - 若想"让值在编译期确定,参与编译期计算",用
constexpr。