C++26:核心语言中更多的 constexpr

Sandor Dargo 22 小时前 2025-04-23T00:00:00+02:00 3 分钟

自从 C++11 中添加了 constexpr 以来,它的范围一直在逐步扩大。最初,我们甚至不能使用 ifelse 或循环,这些在 C++14 中得到了改变。C++17 增加了对 constexpr lambda 的支持。C++20 增加了分配和在常量表达式中使用 std::vectorstd::string 的能力。在本文中,让我们看看 constexpr 如何随着 C++26 发展。更准确地说,让我们看看哪些语言特性变得更加 constexpr 友好。我们将在另一篇文章中讨论库的更改,以及 constexpr 异常,这需要语言和库的共同修改。

P2738R1:从 void* 进行 constexpr 转换

由于 P2738R1 的接受,从 C++26 开始,如果该地址的对象类型与 T 的类型完全相同,则可以在常量表达式中将 void* 转换为 T 类型的指针。

请注意,不允许转换为可互换的类型(包括指向基类的指针)或不相关的类型。

此更改背后的动机是使几个标准库函数或类型在编译时工作。举几个例子:std::formatstd::functionstd::function_refstd::any。此更改将允许标准库中更多 constexpr 的原因是,存储 void* 是一种常用的编译防火墙技术,可以减少模板实例化和编译二进制文件中的符号数量。

P2747R2constexpr placement new

由于 std::construct_at 是一个有限的工具,只允许执行值初始化,而不允许其他初始化(如默认初始化或列表初始化),因此需要使 placement new 在常量表达式中可用。

同时,placement new 是一个非常灵活的工具,甚至可能过于灵活,以安全的方式使用它需要转换为 void*,然后再转换回 T*。这面临一些问题,但 P2738R1 的接受以及在常量表达式中从 void* 转换的能力使不可能成为可能。

如果您正在寻找更多详细信息,请查看 P2747R2

P2686R5:constexpr 结构化绑定和对 constexpr 变量的引用

这是一个相当长的(20 页)提案,我发现它读起来不是特别容易。这不是作者的错,问题很难解决。该论文基于另一篇论文,经历了 5 次修订,讨论了各种解决方案,并在 10 多页上列出了措辞更改。

长话短说,您将能够声明结构化绑定 constexpr

由于结构化绑定的行为类似于引用,因此适用与 constexpr 引用相同的限制。这些限制变得更加宽松。以前,constexpr 引用必须绑定到具有静态存储持续时间的变量,以便地址不会因一次评估而改变。使用 C++26,此外,如果地址相对于引用或结构化绑定所在的堆栈帧是常量,则也接受具有自动存储持续时间的变量。

实际上,这意味着您不能在 lambda 中使用 constexpr 引用来绑定到封闭函数。原因是,为了访问该变量,表达式类似于 this->__x,其中 __x 表示 x 的捕获地址。由于我们在编译时不知道 this 指向什么对象,因此它不是常量表达式。

结论

在本文中,我们回顾了 constexpr 在 C++26 核心语言中是如何演变的。我们获得了从 void*constexpr 转换、placement new、结构化绑定,甚至异常(今天未讨论)。在下一篇文章中,我们将看到标准库的 constexpr 支持如何发展。

深入连接

如果您喜欢这篇文章,请