Jan*_*tke 2 c++ constants global-variables c++-faq linkage
有很多与 C++ 全局变量相关的问题和答案,例如:
它们都包含一些小信息片段,例如inline变量在 C++17 中具体如何工作等。其中一些还没有很好地老化,因为 C++17 通过引入inline变量从根本上改变了规则。
C++20 还引入了模块和模块链接,这再次使得 StackOverflow 上的大多数内容过时,甚至过时。
本问答旨在统一这些问题,并为读者提供考虑这些版本更改的概述。
static、inline、extern、const等constexpr作为全局变量?何时使用
static、inline、extern、const等constexpr作为全局变量?
| 全局变量用例 | 常数 | 非常数 |
|---|---|---|
| 单个源文件的本地 (即仅在单个文件中声明和使用 ,不在标头中声明) |
static const、static constexpr(C++11)或const在匿名命名空间中(C++11) |
static,或在匿名命名空间中(C++11) |
| 已声明,未在标头中定义, 在源文件中定义 |
extern const在标题中;const在源代码中,或constexpr(C++11)在源代码中 |
extern在标题中;源代码中很简单 |
| 在标头中定义, 直到 C++17 |
inline使用模板and constor进行模仿constexpr(C++11);或enum仅适用于整数 |
inline用模板模仿 |
自 C++17 起,在标头中定义 |
inline const, 或者inline constexpr |
inline |
| 单个模块的本地(C++20) | const或者constexpr; 可选地 inline |
可选地inline |
| 由模块导出(C++20) | export const或者export inline constexpr |
export; 可选地 inline |
在上述所有情况下,也可以使用constinit(C++20 起)constexpr ,但不能与 组合使用。constinit consthas 它也使用,并且与 不一样constexpr。
注意:该决定也可能根据全局变量是否在动态链接库中定义/使用以及其他因素而改变。
首先,如果全局变量在单个源文件中声明并且仅在单个源文件中使用,那么它必须具有内部链接。全局变量在以下情况下具有内部链接:
staticconst(或constexpr(C++11 起),因为constexpr暗示const)如果它没有内部链接,那么您很容易遇到 ODR 违规。下面的示例格式错误,无需诊断。
// a.cpp
int counter = 0; // FIXME: surround with anonymous namespace, or add 'static'
// b.cpp
long counter = 0; // FIXME: surround with anonymous namespace, or add 'static'
Run Code Online (Sandbox Code Playgroud)
创建变量inline(C++17 起)并不能解决此问题。内部链接使其安全,因为这两个counters 在每个 TU 中都是不同的。
extern有时候,每个人都有一个定义并不重要。例如:
// log.hpp
extern std::ofstream log_file;
// log.cpp
std::ofstream log_file = open_log_file();
Run Code Online (Sandbox Code Playgroud)
log_file将to 的定义放在标头中并因此随处可见是毫无意义的。在性能方面几乎没有什么收获,而且它也将迫使我们open_log_file()在任何地方都可见。
extern constexpr(C++11 起)或extern const constinit(C++20 起) 的注释另一个有效但罕见的用例是extern constexpr(自 C++11 起)(请参阅此答案),即extern const在标头中,constexpr(自 C++11 起)在源中。如果可用,最好通过源代码中的const constinit(Since C++20)来表达。
此模式的目的是避免动态初始化昂贵的初始化查找表,同时保持标头/源分离。
inline(C++17 起)或inline使用模板进行模仿(C++17 之前)有时,到处都有一个定义很重要,例如应该内联的全局常量:
inline constexpr float exponent = 1.25f;
Run Code Online (Sandbox Code Playgroud)
inline与 结合使用吗constexpr?是的你是。考虑以下示例:
constexpr float exponent = 1.25f; // OK so far, but dangerous
// note: 'const float&' might seem contrived because you could work with 'float'.
// However, templates use const& everywhere, so it's very easy to
// run into this case indirectly.
inline const float& foo(const float& x) {
// IFNDR if the definition of foo appears in multiple translation units (TUs).
return std::max(x, exponent);
}
Run Code Online (Sandbox Code Playgroud)
该程序可能格式错误,无需诊断,因为exponent具有内部链接(由于const)并且是每个 TU 中的不同对象。每个定义都foo可能返回对其自身唯一的不同引用exponent,这违反了[basic.def.odr] p14.5
inline变量还不存在。类似的机制一直以模板的形式存在。
// define a wrapper class template with a static data member
template <typename = void>
struct helper { static const float exponent; };
// define the static data member
template <typename T>
struct helper<T>::exponent = 1.25f;
// For convenience, make a reference with internal linkage to it.
// This is safe from ODR violations because
// [basic.def.odr] p14.5.2 has a special case for it, and
// this special case was retroactively applied to all C++ standards
// in the form of a defect report.
static constexpr float& exponent = helper<>::exponent;
// (static const float& prior to C++11)
Run Code Online (Sandbox Code Playgroud)
或者,特别是对于作用域(C++11 起)和无作用域枚举,您可以将定义放入标头中,而不会产生风险:
inline constexpr int array_size = 100; // since C++17
enum { array_size = 100; }; // pre-C++17 alternative
Run Code Online (Sandbox Code Playgroud)
const偶数constexpr(C++11 起)除了1.、2.、3.中的规则外,总是const尽可能地去做。这极大地简化了确保程序正确性的过程,并实现了额外的编译器优化。
有时您不能仅使用简单的表达式来初始化全局变量,如下所示:
std::array<float, 100> lookup;
void init() {
for (std::size_t i = 0; i < lookup.size(); ++i) {
lookup[i] = compute(i);
}
}
Run Code Online (Sandbox Code Playgroud)
但是,不要延迟初始化;而是使用立即调用的 lambda 表达式 (IILE) (C++11 起)或常规函数来执行初始化。
constexpr std::array<float, 100> lookup = [] {
decltype(lookup) result{};
/* ... */
return result;
}();
Run Code Online (Sandbox Code Playgroud)
constor constexpr(自 C++11 起)对链接有什么影响?创建全局变量constor constexpr(自 C++11 起)为其提供内部链接,但如3.中所述,这不会为您简化任何事情。您仍然需要担心链接和 ODR。
如3.中所示,静态数据成员可能不遵循相同的规则。它们与它们所属的类具有相同的链接,具有以下后果:
inline类模板。const并不意味着静态数据成员的内部链接。此外,constexpr对于静态数据成员意味着inline(C++17 起)。
| 归档时间: |
|
| 查看次数: |
681 次 |
| 最近记录: |