c++ - C++ 中如何使用内联说明符来保留一个定义规则?

标签 c++ namespaces constants linkage one-definition-rule

我一直试图弄清楚 inline说明符保留 ODR。到目前为止,我写的所有东西似乎都没有必要,因为包含保护确保定义只包含一次。
假设我在名为 constants.h 的文件中有以下定义

#ifndef CONSTANTS_H
#define CONSTANTS_H

namespace constants {
    inline const double pi { 3.14159255358979323846 };
    inline const double e  { 2.71828182845904523536 };
}

#endif
以我对inline的理解关于 ODR,inline编写说明符以确保这些常量的定义仅在多个翻译单元中初始化一次。因此,如果我将此文件包含在 a.cpp 中和 b.cpp一切都应该很好。
现在,让我们删除 inline关键词。
#ifndef CONSTANTS_H
#define CONSTANTS_H

namespace constants {
    const double pi { 3.14159255358979323846 };
    const double e  { 2.71828182845904523536 };
}

#endif
现在,如果我将其包含在 a.cpp 中和 b.cpp没有问题。我想这是因为包含 guard 确保同一事物的多个定义不会出现两次。
接下来,让我们移除包含守卫
namespace constants {
    const double pi { 3.14159255358979323846 };
    const double e  { 2.71828182845904523536 };
}
还是没问题。也许是因为const默认情况下,限定的变量定义具有内部链接。结果,包括constants.ha.cppb.cpp默认情况下,将这些定义中的每一个都设置为它们各自的翻译单元的内部。
很难打破跨多个翻译单元的 ODR。现在让我们删除 const。
namespace constants {
    double pi { 3.14159255358979323846 };
    double e  { 2.71828182845904523536 };
}
现在! ODR 跨越多个翻译单元。让我们尝试用 inline 解决这个问题这样编译器就知道只定义这些变量一次。
namespace constants {
    inline double pi { 3.14159255358979323846 };
    inline double e  { 2.71828182845904523536 };
}
好的,没有更多错误,这个文件可以再次包含在多个翻译单元中。那么为什么将头文件中的常量声明为 inline 被认为是“最佳实践”? ?打破ODR和inline好像要下很多功夫在包含守卫存在的情况下是多余的。

最佳答案

未使用说明符 extern 声明的常量具有内部链接。
所以所有包含这些声明的编译单元

namespace constants {
    const double pi { 3.14159255358979323846 };
    const double e  { 2.71828182845904523536 };
}
有自己的常数 pi 和 e。
来自 C++ 14 标准(3.5 程序和链接)

3 A name having namespace scope (3.3.6) has internal linkage if it is the name of

(3.2) — a variable of non-volatile const-qualified type that is neither explicitly declared extern nor previously declared to have external linkage; or


与上述声明相反,这些声明
namespace constants {
    double pi { 3.14159255358979323846 };
    double e  { 2.71828182845904523536 };
}
有外部联动。因此,如果这些声明(也是定义)包含在多个编译单元中,则编译器会发出错误消息,因为一个定义规则被破坏。
例如,如果您在未命名的命名空间中声明它们,则可以使上述变量具有内部链接
namespace constants {
    namespace {
        double pi { 3.14159255358979323846 };
        double e  { 2.71828182845904523536 };
    }
}
至于这些声明
namespace constants {
    inline double pi { 3.14159255358979323846 };
    inline double e  { 2.71828182845904523536 };
}
然后可以在多个编译单元中定义具有外部链接的内联变量。此外,应在 ODR 使用的每个编译单元中定义一个内联变量。

关于c++ - C++ 中如何使用内联说明符来保留一个定义规则?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69966903/

相关文章:

c++ - 线程更新类对象

r - 理解 R 函数惰性求值

javascript - 'namespace' 标识符在 Javascript 中有何特殊含义?

static - 在 Flutter 中保留所有常量的最佳做法是什么?

javascript - 为什么 Babel 不能正确处理 const?

c++ - DirectX 屏幕截图 : Why GetBackBuffer always gives me a black image?

c++ - 如何从 SDL_TEXTINPUT 获取 utf-8 输入?

c++ - 线程构建 block 与 MPI,哪个更适合 mt 需要?

python - 如何使调用者的命名空间可用于导入函数中的 IPython 魔法?

delphi - 是否可以在 Delphi 和 FreePascal 中声明数组的 const 而不让元素为常量?