全局和局部变量初始化与 constexpr 的差异背后的基本原理
Rationale behind the difference of global and local variable initialization with constexpr
假设我有一个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();
,那么它将被编译时评估。但是,这使b
const
.如果我想在之后修改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优化器来删除你引以为豪的所有漂亮有时优化的代码。
- 是否可以初始化不可复制类型的成员变量(或基类)
- C++使用整数的压缩数组初始化对象
- C++初始化基类
- 多成员Constexpr结构初始化
- 复制列表初始化的隐式转换的等级是多少
- 内联映射初始化的动态atexit析构函数崩溃
- 如何在C++中初始化嵌套类中的2个memeber
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 没有用于初始化C++中的变量模板的匹配构造函数
- 在未初始化映射的情况下,将值插入到映射的映射中
- C++成员初始化
- 为什么在C++中首先初始化成员类
- 同时具有"聚合初始化"和"模板推导"
- 初始化具有非默认构造函数的std::数组项的更好方法
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 在C和C++中初始化结构中的数组
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 全局和局部变量初始化与 constexpr 的差异背后的基本原理
- 动态编程中这种初始化背后的直觉