【问题标题】:Difference between constexpr and static constexpr global variableconstexpr 和静态 constexpr 全局变量的区别
【发布时间】:2017-08-31 18:02:21
【问题描述】:

在 C++11 标准中,constexprstatic constexpr 全局变量在头文件中定义时有什么区别?更具体地说,当多个翻译单元包含相同的标头时,哪个声明(如果有)可以保证在翻译单元中定义相同的变量?

例如,

cexpr.h:

#ifndef CEXPR_H
#define CEXPR_H

constexpr int cint = 1;
static constexpr int scint = 1;

#endif

a.cpp:

#include "cexpr.h"

b.cpp:

#include "cexpr.h"

【问题讨论】:

  • 没有区别。 constexpr 暗示 constconst 暗示 static
  • 无:变量上的constexpr 暗示const,命名空间范围内的 const 整型变量默认具有内部链接。
  • @cpplearner:尼特:我不会说“const 意味着静态”,因为extern const int 是有效的,但extern static const int 不是——所以static 没有那么多“隐含”,因为它是某种“默认”。
  • @KerrekSB 为了确保我理解,constexprstatic constexpr 都不允许我在不同的翻译单元中获得相同的对象?
  • @Danra:在 C++14 中,不,你必须使用 extern const int 代替(它仍然适合作为定义它的 TU 中的常量表达式,因为你需要一个初始化器可用作常量表达式)。在 C++17 中,您将使用 inline constexpr int a = 10; 来获取单个对象。

标签: c++ c++11 constexpr linkage


【解决方案1】:

在您当前的示例中没有区别:在变量声明中,constexpr 意味着 const,并且命名空间范围内的 const 变量默认具有内部链接(因此添加 static 不会改变任何内容)。

在 C++14 中,您不能将变量声明为 constexpr 并使其具有外部链接,除非您只在一个翻译单元中执行此操作。原因是constexpr变量需要一个初始化器,而带有初始化器的声明就是一个定义,你必须只有一个定义。

然而,你可以做的是使用一个普通的整数常量,你可以将它声明(不定义)为extern,并且在定义它的翻译单元中甚至可以使用它作为常量表达式:

lib.h:

extern const int a;

lib.cpp:

#include "lib.h"

const int a = 10;

int b[a] = {1, 2, 3};   // OK in this translation unit

在 C++17 中,有一个新功能“内联变量”可以让你说:

inline constexpr int a = 10;

这是一个可以重复出现的“内联定义”,每个定义都定义了一个相同的实体(就像语言中所有其他的“内联”实体一样)。

【讨论】:

  • 在 C++17 之前的头文件中可以有一个 constexpr 定义,但是你必须使用各种解决方法,比如有一个 inline constexpr function里面有一个constexpr static 变量。
  • @DanielH:是的,可能,只要您不使用该变量。
  • 我的意思是,在头文件中,inline constexpr int i() {static constexpr int value = 17; return value;} 应该可以工作,然后你可以说i()。使用函数调用语法很奇怪,但我认为它有效。
  • @DanielH:不,对不起,你是对的,那个很好。我对此感到困惑。一个不好的例子是static constexpr int a = 10; inline constexpr const int& f() { return a; }
  • @KerrekSB 是的,您需要将static 变量放在内部函数中。不幸的是,大多数摆脱函数调用语法的尝试也不好,比如inline constexpr const Foo& f() { static constexpr Foo x; return x; } constexpr Foo& x = f();。我认为如果你能让它成为班级成员,你可以做到这一点,虽然我不记得是如何做到的,而且它涉及一些令人费解的重定向。
【解决方案2】:

我认为这篇文章解释的更清楚。 6.8 — Global constants and inline variables

由于 const 全局变量具有内部链接,每个 .cpp 文件都会获得链接器无法看到的全局变量的独立版本。在大多数情况下,因为这些是 const,编译器会简单地将变量优化掉。
术语“优化离开”是指编译器通过以不影响程序输出的方式删除事物来优化程序性能的任何过程。例如,假设您有一些初始化为值 4 的 const 变量 x。无论您的代码在何处引用变量 x,编译器都可以将 x 替换为 4(因为 x 是 const,我们知道它永远不会更改为不同的值) 并避免完全创建和初始化变量。

所以,“cint”和“scint”都是内部链接变量。

C++ 17 之后定义全局变量的最佳实践:

inline constexpr double pi = 0;

工作机制:

C++17 引入了一个新概念,称为内联变量。在 C++ 中,术语内联已经演变为“允许多个定义”。因此,内联变量是允许在多个文件中定义而不违反一个定义规则的变量。内联全局变量默认有外部链接。

内联变量有两个必须遵守的主要限制: 1) 内联变量的所有定义必须相同(否则,将导致未定义的行为)。 2) 内联变量定义(不是前向声明)必须存在于任何使用该变量的文件中。

编译器会将所有内联定义合并到一个变量定义中。这允许我们在头文件中定义变量,并将它们视为 .cpp 文件中只有一个定义。这些变量还在包含它们的所有文件中保留它们的常量。

【讨论】:

    猜你喜欢
    • 2020-04-06
    • 2014-06-25
    • 2020-10-08
    • 2017-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-26
    相关资源
    最近更新 更多