对于像下面这样的整型类型定义编译时常量(在函数和类范围内),哪种语法最好?
static const int kMagic = 64; // (1)
constexpr int kMagic = 64; // (2)
(1)
也适用于 C++98/03 编译器,而 (2)
至少需要 C++11。两者之间还有其他区别吗?现代 C++ 代码中应该首选其中之一吗?为什么?
编辑
我用 Godbolt 的 CE 尝试了这个示例代码:
int main()
{
#define USE_STATIC_CONST
#ifdef USE_STATIC_CONST
static const int kOk = 0;
static const int kError = 1;
#else
constexpr int kOk = 0;
constexpr int kError = 1;
#endif
return kOk;
}
对于
static const
情况,这是 GCC 6.2 生成的程序集:
main::kOk:
.zero 4
main::kError:
.long 1
main:
push rbp
mov rbp, rsp
mov eax, 0
pop rbp
ret
另一方面,对于
constexpr
来说:
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 0
mov DWORD PTR [rbp-8], 1
mov eax, 0
pop rbp
ret
虽然在
-O3
在这两种情况下我都得到相同的(优化的)组件:
main:
xor eax, eax
ret
编辑#2
我尝试了这个简单的代码(在 Ideone 上运行):
#include <iostream>
using namespace std;
int main() {
const int k1 = 10;
constexpr int k2 = 2*k1;
cout << k2 << '\n';
return 0;
}
这表明
const int k1
在编译时进行评估,因为它用于计算constexpr int k2
。
但是,double
似乎有一种“不同”的行为。我为此创建了一个单独的问题
here。
constexpr
变量保证在
编译时有一个可用的值,而
static const
成员或const
变量可以表示编译时值或运行时值。 constexpr
以比 const
更明确的方式表达编译时值的意图。还有一件事:在 C++17 中,constexpr
静态数据成员变量也将是
内联。这意味着您可以省略
static constexpr
变量的异常定义,但不能省略 static const
。
作为评论区的需求,这里对函数作用域中的static const
进行更详细的解释。
函数作用域中的
static const
变量几乎相同,但它没有自动存储持续时间,而是具有 static
存储持续时间。这意味着它在某种程度上相当于将变量声明为全局变量,但只能在函数中访问。确实,
static
变量在函数的第一次调用时被初始化,但由于它也是const
,编译器将尝试内联该值并完全优化该变量。因此,在函数中,if
在编译时已知该特定变量的值,那么编译器很可能会对其进行优化。但是,如果在编译时在函数作用域中未知
static const
的值,则可能会默默地
使您的函数(非常小一点)变慢,因为它必须在运行时初始化该值第一次调用该函数。另外,它必须在每次调用函数时检查该值是否已初始化。 这就是
constexpr
变量的优点。如果在编译时未知该值,则这是一个“编译错误”,而不是一个较慢的函数。然后,如果您无法在编译时确定变量的值,那么编译器会告诉您这一点,您可以采取一些措施。只要我们讨论声明 scalar 整数或枚举类型的编译时常量,使用
static const
)或 constexpr
之间绝对没有区别。
请注意,编译器需要支持常量表达式中的
static const int
对象(用常量初始值设定项声明),这意味着它们别无选择,只能将此类对象视为编译时常量。此外,只要此类对象保持 odr-unused,它们就不需要定义,这进一步表明它们不会被用作运行时值。此外,
常量初始化
的规则可以防止本地
static const int
对象被动态初始化,这意味着在本地声明此类对象不会造成性能损失。此外,积分 static
对象对静态初始化的排序问题的免疫力是该语言的一个非常重要的特性。
constexpr
是最初通过带有常量初始值设定项的
const
在 C++ 中实现的概念的扩展和概括。对于整数类型,constexpr
不提供任何超出
const
已经提供的功能。
constexpr
只是对初始化器的“常量性”进行早期检查。然而,有人可能会说
constexpr
是专门为此目的而设计的功能,因此它更适合风格。