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

时间:2016-11-19 07:19:26

标签: c++ definition constexpr c++17

考虑以下代码:

#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;
}

无法链接(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 - A::kDefaultValueint来修复。或者将kDefaultValue移出A。两个案件似乎都很难看。这是另一种使其链接的方式吗?

2 个答案:

答案 0 :(得分:13)

这种行为让我一次又一次烦恼。麻烦的原因是你的

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

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

const int A::kDefaultValue;

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

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

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

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

答案 1 :(得分:10)

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

  

如果静态数据成员被声明为constexpr,则它是隐式内联的,不需要在命名空间范围内重新声明。没有初始化程序(以前需要如上所示)的重新声明仍然是允许的,但已被弃用。 (自C ++ 17起)

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

LIVE demo with gcc7