consteval函数必须且只能在编译期求值,否则直接编译失败;其参数须为字面类型且实参为常量表达式,函数体内禁止运行时行为、this指针及非consteval/constexpr调用。

consteval 不是“尝试强制”,而是“编译器一旦发现它可能被运行时调用,立刻报错”。你写一个 consteval 函数,就等于签了一份契约:所有调用点都必须能静态推导出结果,否则连编译都过不去。
consteval 函数为什么一调就报错?常见错误现象
最典型的错误信息是:error: call to consteval function 'xxx' is not a constant expression。这不是警告,是硬性拦截。
- 传入了非常量表达式:比如
int x = 5; f(x);,哪怕x看起来是字面量,但它不是常量表达式(没加constexpr或constinit) - 参数类型不是字面类型(literal type):例如传入
std::string(非字面)、std::vector(含动态内存) - 函数体内调用了非
consteval/constexpr函数,比如std::sqrt(标准库多数数学函数在 C++20 尚未constexpr化) - 用了
static变量、try/catch(除非捕获的是编译期可判定的类型)、asm、new等运行时机制
consteval 和 constexpr 的关键区别在哪
区别不在“能不能编译期求值”,而在“允不允许运行时退路”:
-
constexpr int f(int x) { return x * x; }:可以用于static_assert(f(3) == 9),也可以用于int y = f(x);(x是运行时变量) -
consteval int f(int x) { return x * x; }:只能出现在需要常量表达式的上下文中——模板实参、static_assert、constexpr变量初始化、数组大小等;任何运行时调用直接编译失败 -
consteval函数体内不能有分支逻辑依赖运行时状态(比如if (x > 0)没问题,但if (some_runtime_flag)不合法)
怎么写出真正可用的 consteval 函数
核心是:参数必须是常量表达式,函数体必须只用编译期安全操作。下面这个例子能跑通:
立即学习“C++免费学习笔记(深入)”;
consteval int factorial(int n) {
if (n < 0) throw "negative";
int r = 1;
for (int i = 2; i <= n; ++i) r *= i;
return r;
}
<p>constexpr int F5 = factorial(5); // ✅ OK
// int x = 5; auto r = factorial(x); // ❌ 编译失败:x 不是常量表达式</p>- 参数
n必须是编译期已知整数,如字面量、constexpr变量、模板非类型参数 - 循环和条件判断本身不禁止,只要控制流能被编译器完全展开或裁剪(比如
n是3,那循环就只展开 2 次) - 异常抛出仅限于编译期可捕获的类型(C++23 支持更完整的编译期异常,但当前主流编译器仍建议避免)
- 别忘了编译器要求:GCC 10+ / Clang 11+ / MSVC 19.30+,且显式启用
-std=c++20
consteval 容易被忽略的硬约束
很多人以为写了 consteval 就万事大吉,其实最容易栽在三个地方:
- 头文件里定义了
consteval函数,但没加inline—— 多个 TU 包含会 ODR-violation;C++20 要求consteval函数隐式inline,但某些旧版工具链仍需手动补 - 把
consteval当作性能优化开关:它不提速,只做校验;真要提速得配合查找表、预计算结构体等,consteval只是确保这些结构体一定生成在编译期 - 误以为
if consteval(C++23)能用在 C++20 项目里:它不存在于 C++20,强行用会编译失败;C++20 中替代方案仍是std::is_constant_evaluated()+constexpr分支


















