C++中如何使用内联说明符来保留一个定义规则?
How is the inline specifier used in C++ to preserve the 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
根据我对与 ODR 相关的 inline
的理解,编写 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
中,则没有问题。我想这是因为 include guards 确保同一事物的多个定义不会出现两次。
接下来,让我们移除包含防护
namespace constants {
const double pi { 3.14159255358979323846 };
const double e { 2.71828182845904523536 };
}
还是没问题。可能是因为 const
限定的变量定义默认具有内部链接。因此,在 a.cpp
和 b.cpp
中包含 constants.h
使得这些定义中的每一个在默认情况下都在其各自的翻译单元中。
很难跨多个翻译单元打破 ODR。现在让我们删除常量。
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
在存在 include 守卫的情况下是多余的。
未使用说明符 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 的每个编译单元中定义一个内联变量。
我一直在努力弄清楚 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
根据我对与 ODR 相关的 inline
的理解,编写 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
中,则没有问题。我想这是因为 include guards 确保同一事物的多个定义不会出现两次。
接下来,让我们移除包含防护
namespace constants {
const double pi { 3.14159255358979323846 };
const double e { 2.71828182845904523536 };
}
还是没问题。可能是因为 const
限定的变量定义默认具有内部链接。因此,在 a.cpp
和 b.cpp
中包含 constants.h
使得这些定义中的每一个在默认情况下都在其各自的翻译单元中。
很难跨多个翻译单元打破 ODR。现在让我们删除常量。
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
在存在 include 守卫的情况下是多余的。
未使用说明符 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 的每个编译单元中定义一个内联变量。