C++ 关键字 constexpr 和 consteval 之注意事项

C++ 支持两种不可变性概念(即状态不可变的对象):

const :其大致含义是"我承诺不会更改此值"。它主要用于定义接口,以便在通过指针和引用将数据传递给函数时,无需担心数据会被修改。编译器会强制执行由 const 所作的这一承诺。const 变量的值可以在运行时计算得出。

constexpr :其大致含义是"在编译时进行求值"。它主要用于定义常量,以便将数据存放在只读内存中(从而降低数据被破坏的风险),同时也旨在提升性能。constexpr 变量的值必须由编译器在编译时计算得出。

例如:

cpp 复制代码
	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) 不是一个常量表达式

若要使函数可在常量表达式中使用(即在由编译器进行求值的表达式中),该函数必须定义为 constexprconsteval

。例如:

cpp 复制代码
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 不是一个常量, 因此square(var) 不是一个常量,无法在编译时进行计算
const double max3 = 1.4*square(var); // 可行: 可以在运行时计算

constexpr 函数可以接受非常量实参,但在这种情况下,其求值结果将不再是常量表达式。我们允许在不强制要求常量表达式的语境中,调用带有非常量表达式实参的 constexpr 函数。这样一来,我们便无需重复定义本质上完全相同的函数------即无需分别为常量表达式和变量各定义一个版本。若我们希望某个函数仅用于编译期求值,则应将其声明为 consteval ,而非 constexpr。例如:

cpp 复制代码
consteval double square2(double x) { return x*x; }
constexpr double max1 = 1.4*square2(17); // 可行: 1.4*square(17) 是一个常量表达式
const double max3 = 1.4*square2(var); // 错误: var 的定义不是一个常量而是变量

声明为 constexprconsteval 的函数,是 C++ 对"纯函数(pure functions)"这一概念的实现。它们不得产生副作用,且只能通过参数传递给它们的信息。具体而言,它们不能修改非局部变量,但允许包含循环结构并使用自身的局部变量。

例如:

cpp 复制代码
constexpr double nth(double x, int n) // 假设 0<=n
{
double res = 1;
int i = 0;
while (i<n) { // while循环: 条件为真则执行
res *= x;
++i;
}
return res;
}

在某些语境下,语言规则强制要求使用常量表达式(例如:数组边界 (§1.7)、case 标签 (§1.8)、模板值实参 (§7.2),以及使用 constexpr 声明的常量)。而在其他情况下,编译期求值对于提升性能至关重要。即便暂不考虑性能因素,不可变性(即对象处于一种不可更改的状态)这一概念本身也是一个重要的设计考量。

注意: consteval 是 C++20 新增的一个关键字,其旨在强制函数编译时求值(即将函数声明为一个"即时"函数),且只能用于函数或函数模板,其调用参数必须是常量,不能是变量,是对 constexpr 的进一步限制。例如:

cpp 复制代码
consteval int sqr(int n)
{
	return n * n;  //调用时参数必须是常量
}
constexpr int r = sqr(100); // 可行:编译时计算,参数是常量

int x = 100;
int r2 = sqr(x);            // 错误: 调用不会产生一个常量,参数是变量

const int x = 100;        //可行:参数是常量
int r2 = sqr(x);
constexpr int r3 = sqr(x);
const int r4 = sqr(x);    

consteval int sqrsqr(int n)
{
	return sqr(sqr(n));     // 这时不是一个常量表达式, 但可行,因为调用者是 consteval 函数
}

constexpr int dblsqr(int n)
{
	return 2 * sqr(n);      // 错误: 其外围函数不是一个 consteval
	// 且 sqr(n) 不是一常量,无法在编译时进行计算
}

consteval int f() { return 42; } //可行
consteval auto g() { return &f; }
consteval int h(int (*p)() = g()) { return p(); }
constexpr int r = h();  // 可行
constexpr auto e = g(); // 格式错误:一个立即函数指针不允许作为一个常量表达式结果
相关推荐
黑马程序员毕设29 分钟前
基于Java的医院药品管理系统的优化设计与实现
java·开发语言·spring boot·小程序·架构·课程设计·毕设
_Twink1e1 小时前
openJiuwen 实训营 Day 1 · WorkSwarm 入门教程
开发语言·c++·openjiuwen
问天_观心1 小时前
大模型微调学习(一)
开发语言·人工智能·学习·语言模型·github
阿里嘎多学长1 小时前
2026-09-03 GitHub 热点项目精选
开发语言·程序员·github·代码托管
dong_junshuai2 小时前
每天一个开源项目#87 fmt:24.5K Stars 的 C++ 格式化核心
c++·开源·github
源代码•宸2 小时前
前置准备:定时微服务背景和现状
开发语言·经验分享·后端·微服务·云原生·架构·golang
2601_967338712 小时前
C#上位机开发零基础入门到精通全套视频
开发语言·c#
雪的季节3 小时前
Multisim14.0的详细中文安装步骤【附安装包】
c++
小白学大数据3 小时前
超简单:用 Python 让 Excel 飞起来:用 openpyxl 把重复报表整理交给脚本
开发语言·数据库·python·excel
旧梦95273 小时前
Java SortedMap 接口详解:从入门到实战
java·开发语言