全局和局部变量初始化与 constexpr 的差异背后的基本原理

Rationale behind the difference of global and local variable initialization with constexpr

本文关键字:背后 初始化 局部变量 constexpr 全局      更新时间:2023-10-16

假设我有一个constexpr函数:

constexpr int func() { return 42; }

现在,如果我定义一个用func()初始化的全局变量,那么可以保证func()将被编译时计算,因为变量需要静态初始化:

int a = func(); // here, func() will be compile-time evaluated
// note that a is NOT constexpr

但是,如果我初始化函数中的局部变量,则不能保证func()将在编译时进行评估:

int foo() {
int b = func(); // no guarantee that func() will be compile-time evaluated
return b;
}

我明白,如果我使用constexpr int b = func();,那么它将被编译时评估。但是,这使bconst.如果我想在之后修改b怎么办?

为什么这两种情况有区别?

一旦你把b放在堆栈上,你可以修改它,除非你使用int b const = foo ();。将这样的功能标记为constexpr而不是const没有任何好处。对于现代 CPU 上的大多数目的,constexpr 是一种减少启动时间的方法,这对于处理器和内存较慢的智能手机来说更为重要。

对于嵌入式系统,我们使用constexpr来更轻松地将内容放入ROM。替代方法创建一个脚本,为您打印出 C/C++ 代码,但这是一个额外的步骤。你不会在PC/服务器/智能手机上考虑这一点,因为你使用new来创建动态内存,但在大多数嵌入式系统上你不能这样做(但随着微控制器上更多的RAM和ROM而改变(,但是对于嵌入式系统,你有不同类型的内存,你需要把东西放在RAM或ROM中。这是上下文是天赐之物的地方,在高性能计算中也是如此,但是编译器在const int foo () { return 420; }constexpr foo () { return 420; }之间没有区别,它们都被替换为单个移动指令,而无需运行O2优化器。

到目前为止,我最喜欢的嵌入式系统constexpr用途是确保ROM中只有一个远程过程调用(RPC(标头的副本。另一种方法是使用字符串,这些字符串最终可能会也可能不会重复。constexpr 允许您使用可变参数模板来创建 int 以外的类型数组,这在历史上非常乏味,需要多个步骤。

C++优化的黄金法则是,不要试图用别人教给你的更快的方式来欺骗编译器,这种方式在 15 年前效果很好。相反,您应该指示编译器您希望它做什么,打开 O2 优化,优化编译器将放弃您的代码并将其替换为针对该 CPU#LikeABoss优化的版本。不要像我一样,使用指针算法来设计一个庞大的套接字API,浪费数百小时手动优化代码,只到O2优化器来删除你引以为豪的所有漂亮有时优化的代码。