【问题标题】:constexpr vs. static const: Which one to prefer?constexpr 与 static const:更喜欢哪一个?
【发布时间】:2017-04-28 18:47:08
【问题描述】:

对于定义如下整数类型的编译时常量(在函数和类范围内),哪种语法最好?

static const int kMagic = 64; // (1)
constexpr int kMagic = 64;    // (2)

(1) 也适用于 C++98/03 编译器,而 (2) 至少需要 C++11。两者之间还有其他区别吗?在现代 C++ 代码中应该首选其中一个吗?为什么?


编辑

我用Godbolt's 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

我试过这个简单的代码(live on 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

不过,doubles 的行为似乎不同。我为 here 创建了一个单独的问题。

【问题讨论】:

  • 我更喜欢constexpr 方法。它是显式strong。无论如何,从链接的角度来看,另一种选择也不相同:如果您输入const(或static,或两者兼而有之),它会使变量具有内部链接。所以它们在语义上是不同的。
  • 更喜欢哪一个取决于您将它们用于什么。正如@Nawaz 所说,这两个示例做了不同的事情,如果不提及它们的预期用途,就不可能决定哪个“更好”。
  • 在什么范围内定义它们?
  • @Mr.C64 当你创建一些类,它会被用作模板的参数时,这是使用 constexpr 的好时机:所有成员都是 constexpr,而 static const 不能做这个。 ---- 这就是我想说的。
  • 您编辑中的 GCC 程序集证明不了任何事情。

标签: c++ c++11 constants constexpr


【解决方案1】:

constexpr 变量保证在编译时有一个可用的值。而static const 成员或const 变量可能表示编译时值或运行时值。键入 constexpr 以比 const 更明确的方式表达您对编译时值的意图。

还有一点,在 C++17 中,constexpr 静态数据成员变量也将是内联的。这意味着您可以省略 static constexpr 变量的行外定义,但不能省略 static const


作为评论部分的需求,这里对static const在函数范围内做一个更详细的解释。

函数范围内的static const 变量几乎相同,但它没有自动存储持续时间,而是具有静态存储持续时间。这意味着它在某种程度上相当于将变量声明为全局变量,但只能在函数中访问。

static 变量确实在第一次调用函数时被初始化,但由于它也是const,编译器将尝试内联该值并完全优化该变量。所以在一个函数中,如果在编译时这个特定变量的值是已知的,那么编译器很可能会优化它。

但是,如果在编译时不知道函数范围内 static const 的值,它可能默默地使你的函数(非常小的一点)变慢,因为它必须初始化运行时第一次调用函数时的值。另外,每次调用函数时都要检查值是否被初始化。

这就是constexpr 变量的优势。如果在编译时该值未知,则这是编译错误,而不是较慢的函数。那么如果你在编译时无法确定变量的值,那么编译器会告诉你,你可以做点什么。

【讨论】:

  • 我不是在问“constexpr”与“const”,而是与 static const。 “static const”编译时不是像“constexpr”吗?
  • @NathanOliver:好点。但是,如果它是 static const 类成员,我认为它可以在常量表达式中使用。
  • @Mr.C64:关键字static与编译时const-ness可计算性无关我>。在命名空间级别,它会影响链接;在类范围内,它使成员 common 对所有实例;在函数范围内,它使变量在调用之间persist
  • @Mr.C64:重点是,static const 可以是编译时常量,不是是否意味着它必须是
  • 添加“慢一点”部分:不仅因为它必须在第一次调用函数时初始化值,而且还必须在每次调用时在运行时确定 是否 那是它第一次被调用。这不是微不足道的,请参阅godbolt.org/g/wBejUj
【解决方案2】:

只要我们谈论声明 scalar 整数或枚举类型的编译时常量,使用const(在类范围内为static const)或constexpr 绝对没有区别.

请注意,编译器需要在常量表达式中支持static const int 对象(用常量初始化器声明),这意味着它们别无选择,只能将此类对象视为编译时常量。此外,只要此类对象保持 odr-unused,它们就不需要定义,这进一步表明它们不会用作运行时值。

此外,常量初始化规则防止本地static const int 对象被动态初始化,这意味着在本地声明此类对象不会降低性能。此外,整数static 对象对静态初始化的排序问题的免疫力是该语言的一个非常重要的特性。

constexpr 是最初在 C++ 中通过 const 使用常量初始化器实现的概念的扩展和概括。对于整数类型,constexpr 没有提供比 const 已经提供的任何额外功能。 constexpr 只是对初始化程序的“常量”进行早期检查。但是,有人可能会说 constexpr 是专门为此目的设计的功能,因此它更适合文体。

【讨论】:

  • 感谢您的回答。顺便说一句,seems doubleint 在初始化 constexprconst 方面得到了不同的处理(但不是在 MSVC 中)。
  • @Mr.C64:是的。在我的答案中使用术语“标量”类型是错误的。完整的“编译时间常数”处理仅适用于 const 整数或枚举类型的对象。
  • 谢谢。喜欢constexpr的另一个原因。
  • “常量初始化规则防止本地 static const int 对象被动态初始化” - 你的意思是如果初始化器是一个常量表达式,对吧?
  • 这不完全一样吧?如果你创建一个变量 constexpr 它将在编译时初始化,这样会增加编译时间,但会使运行时间更快?
猜你喜欢
  • 2016-09-24
  • 2017-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多