Constexpr
本节阅读量:constexpr关键字
当使用const关键字声明const变量时,编译器将隐式跟踪它是运行时常量还是编译时常量。在大多数情况下,运行时常量还是编译时常量对于优化以外的任何事情都无关紧要,但在一些情况下,C++需要常量表达式(我们将在稍后介绍这些主题时介绍这些情况)。并且在常量表达式中只能使用编译时常量。
由于编译时常量还允许更好的优化(并且没有什么缺点),因此我们通常希望尽可能使用编译时常量。
当使用const时,变量是编译时常量或运行时常量,这取决于初始值是否是编译时常量表达式。在某些情况下,很难区分。
例如:
|
|
在上面的示例中,w可以是运行时常量,也可以是编译时常量,具体取决于getValue() 的定义方式。一点也不清楚!
幸运的是,我们可以获得编译器的帮助,以确保在需要的地方获得编译时常量。为此,我们在变量的声明中使用constexpr关键字而不是const。constexpr(“常量表达式”的缩写)变量只能是编译时常量。如果constexpr变量的初始化值不是常量表达式,编译器将报错。
例如:
|
|
最佳实践
任何在初始化后不修改,且其初始值在编译时已知的变量都应声明为constexpr。任何在初始化后不修改,且其初始值在编译时未知的变量都应声明为const。
后续章节,我们将讨论当前与constexpr不兼容的一些类型(包括std::string、std::vector和其他使用动态内存分配的类型)。对于这些类型的常量对象,请改用const。
注
本站点上的许多示例都是在使用constexpr的最佳实践之前编写的——因此,您会注意到,许多示例没有遵循上述最佳实践。我们目前正在更新遇到的不符合示例。
const和constexpr函数参数
普通函数调用在运行时求值。这意味着函数参数即使是编译时常量,也会被视为运行时常量。
由于constexpr对象必须用编译时常量(而不是运行时常量)初始化,因此不能将函数参数声明为constexpr。
相关内容
C++确实支持可以在编译时计算的函数(因此可以在常量表达式中使用)。后续在——constexpr和consteval函数中讨论了这些。
C++还支持将编译时常量传递给函数的方法。我们在——非类型模板参数中讨论了这些。
何时实际计算常量表达式?
编译器在需要将常量表达式结果作为常量时(如编译时常量的初始值)计算常量表达式:
|
|
在不需要结果是常量表达式的上下文中,编译器可以选择是在编译时还是在运行时计算常量表达式。
|
|
在上面的变量定义中,x不是constexpr变量,并且在编译时不需要知道初始化值。因此,编译器可以自由选择是在编译时还是在运行时计算3+4。
尽管它不是严格要求的,但现代编译器通常会在编译时计算常量表达式,因为这样做性能更高。
常量折叠(Constant folding)
考虑以下示例:
|
|
3+4是一个常量表达式,同时编译器将在编译时计算3+4,并将其替换为值7。由于x是编译时常数,编译器可能会完全优化上述程序中的x,将std::cout « x « ‘\n’ 替换为 std::cout « 7 «’\n’。输出表达式将在运行时执行。
然而,由于x仅使用一次,因此我们更有可能首先这样编写程序:
|
|
由于表达式 std::cout « 3 + 4 « ‘\n’ 不是常量表达式,因此有理由怀疑常量子表达式3+4是否仍将在编译时优化。
答案通常是“是”。编译器早就能够优化常量子表达式,即使完整表达式是运行时表达式。这种优化过程称为“常量折叠”。
将变量设为constexpr,可以确保在常量子表达式中使用这些变量时,它们符合常量折叠的条件。
