C++23 中 constexpr 的重要改动
文章目录
- 1. constexpr 函数中使用非字面量变量、标号和 goto (P2242R3)
- 示例代码
- 2. 允许 constexpr 函数中的常量表达式中使用 static 和 thread_local 变量 (P2647R1)
- 示例代码
- 3. constexpr 函数的返回类型和形参类型不必为字面类型 (P2448R2)
- 示例代码
- 4. 不存在满足核心常量表达式要求的调用的 constexpr 函数 (P2448R2)
- 示例代码
- 总结表格
在 C++23 标准中,
constexpr
特性迎来了一系列令人瞩目的改动,这些改动进一步提升了 C++ 的编译时计算能力和代码的灵活性。下面我们将详细介绍这些改动,并通过表格的形式进行总结。
1. constexpr 函数中使用非字面量变量、标号和 goto (P2242R3)
在 C++23 之前,constexpr
函数的使用受到较多限制,不能在其中使用非字面量变量、标号和 goto
语句。但在 C++23 中,这些限制被放宽了。这意味着在 constexpr
函数里,我们可以更自由地编写代码,利用非字面量变量进行计算,使用标号和 goto
语句实现复杂的控制流。
在过去,由于这些限制,一些看似合理的代码可能会被编译器拒绝。例如下面的代码:
template<typename T> constexpr bool f() {if (std::is_constant_evaluated()) {// ...return true;} else {T t;// ...return true;}
}
struct nonliteral { nonliteral(); };
static_assert(f<nonliteral>());
在之前的标准中,这段代码可能会因为 nonliteral
是一个非字面类型而导致编译失败,尽管导致失败的那一行代码并不在常量求值的上下文中。而在 C++23 中,这样的代码是有效的。
从编译器支持情况来看,GCC 12 和 Clang 15 开始支持这一改动。这一改动的原理是,只要这些非字面量变量、标号和 goto
语句在编译时不被求值,它们在函数中的存在就不会有问题。因为 constexpr
函数可能在编译时求值,也可能在运行时求值。如果我们想在 constexpr
函数中调用一段保证在编译时求值的代码,需要将这段代码放在 if consteval
或 if (std::is_constant_evaluated())
条件下的代码块中。
示例代码
#include <iostream>constexpr int func(int x) {int result = 0;if (x > 0) {result = x * 2;} else {// 使用标号和 gotolabel:result = -x;}return result;
}int main() {constexpr int value = func(5);std::cout << "Result: " << value << std::endl;return 0;
}
2. 允许 constexpr 函数中的常量表达式中使用 static 和 thread_local 变量 (P2647R1)
在 C++23 之前,constexpr
函数的常量表达式中不允许使用 static
和 thread_local
变量。C++23 打破了这个限制,允许在 constexpr
函数的常量表达式中使用这两种变量。这为编译时计算提供了更多的可能性,例如可以在编译时初始化一些静态变量或线程局部变量。
最初,constexpr
函数中根本不允许声明任何 static
变量。后来在 [P2242R3] 中有所放宽,规则改为控制流不能经过 static
变量的初始化。对于 static
(或者更糟糕的 thread_local
)变量,其初始化器可能会运行任意代码,所以之前有这样的限制是合理的。但对于 static constexpr
变量,根据定义,它必须是常量初始化的,不存在何时运行初始化的问题,它就是一个常量。
下面我们来看一个例子:
char xdigit(int n) {static constexpr char digits[] = "0123456789abcdef";return digits[n];
}
这个函数原本是完全没问题的,但当我们尝试将其扩展为在编译时也能工作时,就会遇到问题:
constexpr char xdigit(int n) {static constexpr char digits[] = "0123456789abcdef";return digits[n];
}
在之前的标准中,这段代码是格式错误的,但在 C++23 中,它是有效的。
在之前为了实现类似的功能,有几种变通方法,但都有各自的缺点。比如可以完全避开 static
变量,直接索引字面量,但这只在我们只需要使用一次时才有效:
constexpr char xdigit(int n) {return "0123456789abcdef"[n];
}
也可以将 static
变量移到非局部作用域,但我们希望将其设为局部变量是有原因的,它只与这个特定的函数相关:
static constexpr char digits[] = "0123456789abcdef";
constexpr char xdigit(int n) {return digits[n];
}
还可以将变量设为非 static
,但编译器很难对其进行优化,会导致代码生成效果变差:
constexpr char xdigit(int n) {constexpr char digits[] = "0123456789abcdef";return digits[n];
}
示例代码
#include <iostream>constexpr int func() {static int counter = 0;counter++;return counter;
}int main() {constexpr int value = func();std::cout << "Counter value: " << value << std::endl;return 0;
}
3. constexpr 函数的返回类型和形参类型不必为字面类型 (P2448R2)
在 C++23 之前,constexpr
函数的返回类型和形参类型必须是字面类型。C++23 放宽了这一要求,允许 constexpr
函数的返回类型和形参类型不必为字面类型。这使得 constexpr
函数的使用更加灵活,可以处理更多类型的数据。
示例代码
#include <iostream>
#include <string>constexpr std::string func(const std::string& str) {return str + " appended";
}int main() {constexpr std::string result = func("Hello");std::cout << "Result: " << result << std::endl;return 0;
}
4. 不存在满足核心常量表达式要求的调用的 constexpr 函数 (P2448R2)
在 C++23 中,对于那些不存在满足核心常量表达式要求的调用的 constexpr
函数,也有了新的处理方式。这使得在某些情况下,即使函数的调用不满足核心常量表达式的要求,函数仍然可以作为 constexpr
函数存在。
示例代码
#include <iostream>constexpr int func(int x) {if (x > 0) {return x * 2;} else {// 这里的调用可能不满足核心常量表达式要求return -x;}
}int main() {int value = 5;// 这里的调用可能不是常量表达式int result = func(value);std::cout << "Result: " << result << std::endl;return 0;
}
总结表格
改动内容 | 提案编号 | 说明 |
---|---|---|
constexpr 函数中使用非字面量变量、标号和 goto | P2242R3 | 放宽了 constexpr 函数的使用限制,允许使用非字面量变量、标号和 goto 语句,只要在编译时不被求值即可,GCC 12 和 Clang 15 开始支持 |
允许 constexpr 函数中的常量表达式中使用 static 和 thread_local 变量 | P2647R1 | 打破了 constexpr 函数常量表达式中对 static 和 thread_local 变量的限制,之前 static 变量相关规则在 [P2242R3] 中有所调整,现在 static constexpr 变量在 constexpr 函数中使用更合理 |
constexpr 函数的返回类型和形参类型不必为字面类型 | P2448R2 | 使 constexpr 函数的使用更加灵活,可处理更多类型的数据 |
不存在满足核心常量表达式要求的调用的 constexpr 函数 | P2448R2 | 对于不满足核心常量表达式要求的调用的 constexpr 函数有了新的处理方式 |
相关文章:

C++23 中 constexpr 的重要改动
文章目录 1. constexpr 函数中使用非字面量变量、标号和 goto (P2242R3)示例代码 2. 允许 constexpr 函数中的常量表达式中使用 static 和 thread_local 变量 (P2647R1)示例代码 3. constexpr 函数的返回类型和形参类型不必为字面类型 (P2448R2)示例代码 4. 不存在满足核心常量…...
CMake ctest
CMake学习–ctest全面介绍 1. 环境准备 确保已安装 cmake 和编译工具: sudo apt update sudo apt install cmake build-essential2. 创建示例项目 假设我们要测试一个简单的数学函数 add(),项目结构如下: math_project/ ├── CMakeList…...

全面解析React内存泄漏:原因、解决方案与最佳实践
在开发React应用时,内存泄漏是一个常见但容易被忽视的问题。如果处理不当,它会导致应用性能下降、卡顿甚至崩溃。由于React的组件化特性,许多开发者可能没有意识到某些操作(如事件监听、异步请求、定时器等)在组件卸载…...
JavaScript学习教程,从入门到精通,Ajax数据交换格式与跨域处理(26)
Ajax数据交换格式与跨域处理 一、Ajax数据交换格式 1. XML (eXtensible Markup Language) XML是一种标记语言,类似于HTML但更加灵活,允许用户自定义标签。 特点: 可扩展性强结构清晰数据与表现分离文件体积相对较大 示例代码࿱…...

【FreeRTOS】事件标志组
文章目录 1 简介1.1事件标志1.2事件组 2事件标志组API2.1创建动态创建静态创建 2.2 删除事件标志组2.3 等待事件标志位2.4 设置事件标志位在任务中在中断中 2.5 清除事件标志位在任务中在中断中 2.6 获取事件组中的事件标志位在任务中在中断中 2.7 函数xEventGroupSync 3 事件标…...

超级扩音器手机版:随时随地,大声说话
在日常生活中,我们常常会遇到手机音量太小的问题,尤其是在嘈杂的环境中,如KTV、派对或户外活动时,手机自带的音量往往难以满足需求。今天,我们要介绍的 超级扩音器手机版,就是这样一款由上海聚告德业文化发…...

【数据可视化-27】全球网络安全威胁数据可视化分析(2015-2024)
🧑 博主简介:曾任某智慧城市类企业算法总监,目前在美国市场的物流公司从事高级算法工程师一职,深耕人工智能领域,精通python数据挖掘、可视化、机器学习等,发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…...

【6G 开发】NV NGC
配置 生成密钥 API Keys 生成您自己的 API 密钥,以便通过 Docker 客户端或通过 NGC CLI 使用 Secrets Manager、NGC Catalog 和 Private Registry 的 NGC 服务 以下个人 API 密钥已成功生成,可供此组织使用。这是唯一一次显示您的密钥。 请妥善保管您的…...
计算机视觉各类任务评价指标详解
文章目录 计算机视觉各类任务评价指标详解一、图像分类(Image Classification)常用指标1. 准确率(Accuracy)2. Top-k Accuracy3. 精确率(Precision)、召回率(Recall)、F1 分数&#…...

SIEMENS PLC程序解读 -Serialize(序列化)SCATTER_BLK(数据分散)
1、程序数据 第12个字节 PI 2、程序数据 第16个字节 PI 3、程序数据 第76个字节 PO 4、程序代码 2、程序解读 图中代码为 PLC 梯形图,主要包含以下指令及功能: Serialize(序列化): 将 SRC_VARIABLEÿ…...

宁德时代25年时代长安动力电池社招入职测评SHL题库Verify测评语言理解数字推理真题
测试分为语言和数字两部分,测试时间各为17分钟,测试正式开始后不能中断或暂停...
python源码打包为可执行的exe文件
文章目录 简单的方式(PyInstaller)特点步骤安装 PyInstaller打包脚本得到.exe文件 简单的方式(PyInstaller) 特点 支持 Python 3.6打包为单文件(–onefile)或文件夹形式自动处理依赖项 步骤 安装 PyIns…...
数据加密技术:从对称加密到量子密码的原理与实战
数据加密技术:从对称加密到量子密码的原理与实战 在网络安全体系中,数据加密是保护信息机密性、完整性的核心技术。从古代的凯撒密码到现代的量子加密,加密技术始终是攻防博弈的关键战场。本文将深入解析对称加密、非对称加密、哈希函数的核…...
高性能的开源网络入侵检测和防御引擎:Suricata介绍
一、Debian下使用Suricata 相较于Windows,Linux环境对Suricata的支持更加完善,操作也更为便捷。 1. 安装 Suricata 在Debian系统上,你可以通过包管理器 apt 轻松安装 Suricata。 更新软件包列表: sudo apt update安装 Suricata: sudo apt …...

【硬核解析:基于Python与SAE J1939-71协议的重型汽车CAN报文解析工具开发实战】
引言:重型汽车CAN总线的数据价值与挑战 随着汽车电子化程度的提升,控制器局域网(CAN总线)已成为重型汽车的核心通信网络。不同控制单元(ECU)通过CAN总线实时交互海量报文数据,这些数据隐藏着车…...
React类组件与React Hooks写法对比
React 类组件 vs Hooks 写法对比 分类类组件(Class Components)函数组件 Hooks组件定义class Component extends React.Componentconst Component () > {}状态管理this.state this.setState()useState()生命周期componentDidMount, componentDidU…...

Uniapp 自定义 Tabbar 实现教程
Uniapp 自定义 Tabbar 实现教程 1. 简介2. 实现步骤2.1 创建自定义 Tabbar 组件2.2 配置 pages.json2.3 在 App.vue 中引入组件 3. 实现过程中的关键点3.1 路由映射3.2 样式设计3.3 图标处理 4. 常见问题及解决方案4.1 页面跳转问题4.2 样式适配问题4.3 性能优化 5. 扩展功能5.…...

记录一次使用面向对象的C语言封装步进电机驱动
简介 (2025/4/21) 本库对目前仅针对TB6600驱动下的42步进电机的基础功能进行了一定的封装, 也是我初次尝试以面向对象的思想去编写嵌入式代码, 和直流电机的驱动步骤相似在调用stepmotor_attach()函数和stepmotor_init()函数之后仅通过结构体数组stepm然后指定枚举变量中的id即…...

Spark-streaming核心编程
1.导入依赖: <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming-kafka-0-10_2.12</artifactId> <version>3.0.0</version> </dependency> 2.编写代码: 创建Sp…...
Exposure Adjusted Incidence Rate (EAIR) 暴露调整发病率:精准量化疾病风险
1. 核心概念 1.1 传统发病率的局限性 1.1.1 公式与定义 传统发病率公式为新发病例数除以总人口数乘以观察时间。例如在某社区观察1年,有10例新发病例,总人口1000人,发病率即为10/10001=0.01。 此公式假设所有个体暴露时间和风险相同,但实际中个体差异大,如部分人暴露时间…...

vue3+TS+echarts 折线图
需要实现的效果如下 <script setup lang"ts" name"RepsSingleLineChart">import * as echarts from echartsimport { getInitecharts } from /utils/echartimport type { EChartsOption } from echarts// 定义 props 类型interface Props {id: strin…...
MYSQL中为什么不建议delete数据
在 MySQL 中不建议频繁使用 delete 删除数据的原因主要在于性能、数据安全等方面的问题,以下是具体介绍: 性能问题 磁盘空间与碎片:delete 操作只是将数据标记为 “已删除”,并不会立即释放磁盘空间,频繁执行会导致大量…...
Linux多线程技术
什么是线程 在一个程序里的多执行路线就是线程。线程是进程中的最小执行单元,可理解为 “进程内的一条执行流水线”。 进程和线程的区别 进程是资源分配的基本单位,线程是CPU调度的基本单位。 fork创建出一个新的进程,会创建出一个新的拷贝&…...
12个HPC教程汇总!从入门到实战,覆盖分子模拟/材料计算/生物信息分析等多个领域
在科学研究、工程仿真、人工智能和大数据分析等领域,高性能计算 (High Performance Computing, HPC) 正扮演着越来越重要的角色。它通过并行处理、大规模计算资源的整合,极大提升了计算效率,使原本耗时数日的任务能够在数小时内完成。 随着计…...
[OpenGL] Lambertian材质漫反射BRDF方程的解释与推导
一、简介 本文简单的介绍了 Physical Based Rendering, PBR 中的 Lambertian 材质漫反射BRDF公式 f r l a m b e r t i a n c d i f f π fr_{lambertian}\frac{c_{diff}}{\pi} frlambertianπcdiff的推导。 二、漫反射项 根据 渲染方程: L o ( v ) ∫ …...

小火电视桌面TV版下载-小火桌面纯净版下载-官方历史版本安装包
别再费心地寻找小火桌面的官方历史版本安装包啦,试试乐看家桌面吧,它作为纯净版本的第三方桌面,具有诸多优点。 界面简洁纯净:乐看家桌面设计简洁流畅,页面简洁、纯净无广告,为用户打造了一个干净的电视操…...
VSFTPD+虚拟用户+SSL/TLS部署安装全过程(踩坑全通)
Author : Spinach | GHB Link : http://blog.csdn.net/bocai8058文章目录 前言准备配置虚拟用户1.创建虚拟用户列表文件2.生成数据库文件3.设置虚拟用户独立访问权限 配置PAM认证1.创建PAM配置文件2.测试PAM认证 创建虚拟用户映射的系统用户生成SSL/TLS证书配置VSFTPD服务1…...
07 Python 字符串全解析
文章目录 一. 字符串的定义二. 字符串的基本用法1. 访问字符串中的字符2. 字符串切片3. 字符串拼接4. 字符串重复5.字符串比较6.字符串成员运算 三. 字符串的常用方法1. len() 函数2. upper() 和 lower() 方法3. strip() 方法4. replace() 方法5. split() 方法 四. 字符串的进阶…...

androidstudio安装配置
B站配置视频AndroidStudio安装配置教程(最新版本教程)3分钟搞定 快速安装使用_哔哩哔哩_bilibili 1、环境变量 D:\AndroidSdk ANDROID_HOME ANDROID_SDK_HOME 2、新建 3、配置 distributionUrlhttps://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-…...
全面解析 MCP(Model Context Protocol):AI 大模型的“万能连接器”
一、MCP 的定义与技术定位 **MCP(Model Context Protocol,模型上下文协议)**是由 Anthropic 公司于 2024 年 11 月推出的开源协议,旨在为 AI 大模型与外部数据源、工具之间建立标准化连接通道。它被业界称为 “AI 的 USB-C 接口”,通过统一的通信协议和数据结构,解决大模…...