我一直在试图弄清楚inline
说明符是如何保留ODR的。到目前为止,对于我所写的所有内容,这似乎都是不必要的,因为include guard确保定义只包含一次。
假设我在一个名为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
中,则没有问题。我想这是因为include保护,它确保同一事物的多个定义不会出现两次。
接下来,让我们删除包含保护
namespace constants {
const double pi { 3.14159255358979323846 };
const double e { 2.71828182845904523536 };
}
还是没问题。可能是因为默认情况下const
限定的变量定义具有内部链接。结果,在a.cpp
和b.cpp
中包括constants.h
使得默认情况下这些定义中的每一个对于它们各自的翻译单元是内部的。
很难在多个翻译单元之间打破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具有命名空间作用域(3.3.6(的名称具有内部链接,如果它是的名称
(3.2(-非易失性常量限定类型的变量既没有显式声明为extern,也没有先前声明为have外部联动;或
与上述声明相反,这些声明
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的每个编译单元中定义内联变量。