在 C++17 中,我们得到了内联变量,并且我假设全局 constexpr 变量是隐式内联的。 但显然这只适用于静态 member 变量。
这背后的逻辑/技术限制是什么?
来源:
声明为 constexpr 的静态成员变量(但不是名称空间范围变量)隐式是内联变量。
这里的要点是,命名空间范围内的
constexpr int x = 1;
在 C++14 中具有内部链接。
如果在不更改内部链接部分的情况下使其隐式内联,则更改不会产生任何效果,因为内部链接意味着无论如何都无法在其他翻译单元中定义它。这会损害可教性,因为我们希望像
inline constexpr int x = 1;
这样的东西默认获得外部链接(毕竟,内联的全部目的是允许在多个翻译单元中定义 same 变量)。
如果你让它隐式地内联于外部链接,那么你就破坏了现有的代码:
// TU1
constexpr int x = 1;
// TU2
constexpr int x = 2;
这个完全有效的 C++14 将成为 ODR 违规。
隐式创建
constexpr
静态数据成员的原因 inline
是为了解决 C++ 中的一个常见问题:在定义类范围常量时,之前必须在一个翻译单元中发出定义,以免变量使用 ODR:
// foo.h
struct foo {
static constexpr int kAnswer = 42;
};
// foo.cpp
// a linker error will occur if this definition is omitted before C++17
#include "foo.h"
constexpr int foo::kAnswer;
// main.cpp
#include "foo.h"
#include <vector>
int main() {
std::vector<int> bar;
bar.push_back(foo::kAnswer); // ODR-use of 42
}
在这种情况下,我们通常只关心常量的值,而不关心它的地址;如果确实使用了 ODR,编译器可以很方便地为常量合成一个唯一的位置,但我们不关心该位置在哪里。
因此,C++17 改变了规则,不再需要外线定义。为此,它使
foo::kAnswer
的声明成为内联定义,以便它可以出现在多个翻译单元中而不会发生冲突,就像内联函数一样。
对于 namespace-scope
constexpr
变量(隐式 static
,因此具有 internal 链接,除非声明 extern
)不存在类似问题。每个翻译单元都有自己的副本。目前指定的 inline
对此类变量没有影响。改变现有的行为会破坏现有的程序。