y0prst y0prst - 21 days ago 7
C++ Question

Undefined reference error for static constexpr member

Consider this code:

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


It fails to link (llvm clang, gcc 4.9, both on 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


The question is what's wrong with it? It can be fixed by
static_cast
-ing
A::kDefaultValue
to
int
. Or by moving
kDefaultValue
out of
A
. Both cases seem to be ugly. Is this another way to make it link?

Answer

This behaviour is vexing me time and again. The cause of the trouble is that your

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

odr-uses the static constexpr member, since the constructor of v takes a constant reference second argument. Odr-usage requires a definition somewhere, i.e.

const int A::kDefaultValue;

in some compilation unit (which is compiled and linked to main()). This requirement has been dropped in C++17 and the corresponding definition (as above) deprecated.

However, a definition is not always possible (for example for members of class templates) and the simplest way to avoid both the definition and your error is

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

which creates a temporary to be passed to the constructor of v (but since the latter is fully inline, the compiler may optimise that away).