静态 constexpr 成员的未定义引用错误

Undefined reference error for static constexpr member

考虑这段代码:

#include <vector>

struct A {
  static constexpr int kDefaultValue = -1;
  std::vector<int> v;
  A(int n): v(n, A::kDefaultValue) {}
};

int main() {
  A(10);
  return 0;
}

它无法 link(llvm clang,gcc 4.9,都在 OS X 上):

Undefined symbols for architecture x86_64:
  "A::kDefaultValue", referenced from:
      A::(int) in main.cpp.o
ld: symbol(s) not found for architecture x86_64

问题是它有什么问题?它可以通过 static_cast-ing A::kDefaultValueint 来修复。或者将 kDefaultValue 移出 A。这两种情况似乎都很丑陋。这是另一种方法吗link?

自 C++17 以来行为发生了变化。在 C++17 之前,即使 constexpr static data member 必须在 class 定义中初始化,仍然需要在命名空间范围内定义;自 C++17 起不再需要命名空间范围定义。

If a static data member is declared constexpr, it is implicitly inline and does not need to be redeclared at namespace scope. This redeclaration without an initializer (formerly required as shown above) is still permitted, but is deprecated. (since C++17)

使用支持 C++17 的编译器编译您的代码可以正常工作。

LIVE demo with gcc7

这种行为一次又一次地困扰着我。麻烦的原因是你的

A(int n): v(n, A::kDefaultValue) {}

odr-uses static constexpr 成员,因为 v 的构造函数采用常量引用第二个参数。 Odr-usage 需要在某处定义,即

const int A::kDefaultValue;

在某些编译单元中(已编译并链接到 main())。此要求已在 C++17 中删除,相应的定义(如上)已弃用。

但是,定义并不总是可行的(例如对于 class 模板的成员),避免定义和错误的最简单方法是

A(int n): v(n, int(A::kDefaultValue)) {}

它创建一个临时传递给 v 的构造函数(但由于后者是完全内联的,编译器可能会优化它)。