用C++ 1z定义全局常量?

Sah*_*Yar 9 c++ constants c++17

C++ 1z中声明内存有效全局常量的最佳方法是什么,它不进行内部链接,因此在所有翻译单元中使用单个副本?

尽管在许多地方已经提到过,但我们没有任何单一的"最佳方法"问题和答案,所以在这里.以下是我找到相关问题的部分地方列表.

我在找为atleast所有基本基本类型的解决方案,如int,double,char,std::string.

编辑1: 最好的方式,我的意思是"记忆效率".我知道对于内部联系,每个翻译单元都会有多个副本,因此会占用内存.除此之外,如果我们能够实现快速的执行时间和编译时间,这将是伟大的,代码的美化(容易)并不重要.此外,我知道外部链接将增加获取时间,因为处理器可能会在运行时获得缓存未命中.但最新的C++语言是否支持所有这些的最佳方法?或者可能建议支持每个案例?

注意:对于std::string全局常量,最佳方法是什么?是否存在可变性会对有任何影响常量性

Dan*_*l H 6

摘要

在C ++ 17中,定义全局常量或全局变量的最简单方法通常是使用内联变量。您只需在头文件中添加如下一行即可创建一个:

inline const std::string greeting = "Hello!";
Run Code Online (Sandbox Code Playgroud)

如果全局常量是文字类型,则最好使用inline constexprinline如果是静态类成员,则是可选的)而不是inline const

inline constexpr std::string_view greeting = "Hello!"sv;
Run Code Online (Sandbox Code Playgroud)

这也适用于变量,但是许多优点不再适用,因此您可能要使用另一种方法:

inline unsigned int score = 0;
Run Code Online (Sandbox Code Playgroud)

细节

首先,此方法的两个主要缺点是:

  1. 它在C ++ 17之前不起作用,因此,如果您需要C ++ 14或更早版本的兼容性,则需要做其他事情。VTT建议模板技巧与旧标准兼容,并且应该具有与内联变量相似的语义,但是如果您只需要C ++ 17支持,就不再需要这种技巧。
  2. 编译比extern变量要慢一些,因为编译器需要合并多个定义,并且因为更多信息可供优化器使用。如果您可以更改定义,但不能更改数据类型,则“有点”部分会变成“明显”部分。由于该值现在位于头文件中,因此这意味着重新编译包括头文件的所有内容,而不仅仅是重新链接。如果您可能需要更改数据类型,则无论如何都需要重新编译所有内容。

如果这两个都不对您很重要,那么我认为该方法优于通过外部链接获得全局常数且在最终可执行文件中最多只有一个定义² 的其他方法。

仅在一个文件中提到了这样的内联变量,因此很容易更改。这对于仅标头的库特别有用。这也意味着它具有在编译时可用的值,因此优化器可以看到它,甚至可以消除一些用法。

使用 constexpr

在C ++ 17中,静态constexpr类成员是自动生成的inline,因此,如果您的全局常量应属于类范围的一部分,则可以执行以下操作

constexpr int favorite_number = -3;
Run Code Online (Sandbox Code Playgroud)

否则,您将需要说constexpr inline,这仍然有效。这将具有上述语义,而且具有的通常优点constexpr,因此编译器将知道它可以在编译时尝试做更多的事情。例如:

#include <string_view>

using namespace std::literals;

inline constexpr std::string_view greeting = "Hello!"sv;

inline constexpr int scrabble_points[greeting.size()] = {4, 1, 1, 1, 1, 0};

int main() {
  int total = 0;
  for (int i : scrabble_points) {
    total += i;
  }
  return total;
}
Run Code Online (Sandbox Code Playgroud)

可以用constexpr,但不能用inline,因为可以,因为constexpr它知道这greeting.size()是一个编译时常量,可以用作数组的大小。³通过优化,可以将其编译为一条mov指令,并且ret不包含任何副本字符串或数组,因为它是不必要的。

使用新的内联语义,以前的所有内容都main可能位于多个位置包含的头文件中,并且最多仍会有一个副本。

变数

相同的方法通过省略const:轻松支持可变变量:

inline std::string player_name = "<subject name here>";
Run Code Online (Sandbox Code Playgroud)

然后,这是具有外部链接的全局变量。由于它是一个变量,因此我在Pete的答案中提到的大多数优点都消失了,但是仍然存在一些优点(例如仅在一个位置声明变量,而无需链接任何其他内容)。但是,它们可能不值得花一点额外的编译时间和缺少C ++ 14兼容性。


¹对于constconstexpr变量,如果不需要,编译器/优化器可能会完全消除该变量。从理论上讲,它可能决定将其复制为立即值或其他值。在实践中,您可能不必担心,因为只有在有充分理由的情况下,它才会这样做,并且这会使最终的可执行文件更小和/或更快速。您可以使用-Os而不是进行调整-O3

²每个使用该常量的目标文件仍将有一个副本,但这些文件将在链接时合并。避免这种情况的唯一方法是使用extern变量。

³这个简化的示例即使在没有inline或仅以const代替数组的情况下constexpr也可以使用,但是对于更复杂的实际情况非常有用。

  • “ constexpr变量自动内联”不,不是。只有`constepxr`静态数据成员。 (2认同)

Pet*_*ker 5

回到过去,我们在头文件中声明一个常量并在源文件中定义它:

// constants.h
extern const int size;

// constants.cpp
#include "constants.h"
const int size = 3;

// usage
std::cout << size << '\n';
Run Code Online (Sandbox Code Playgroud)

但也许这太简单了; 为什么不用10行模板和时髦的实例化语法来支持它呢?

  • @SahibYar - 默认情况下`const`****有内部链接.标记它`extern`给它外部链接.我写的代码没有ODR问题:`size`只在一个地方定义. (5认同)

归档时间:

查看次数:

2374 次

最近记录:

8 年,3 月 前