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?
static defines the object's lifetime during execution; constexpr specifies that the object should be available during compilation. Compilation and execution are disjoint and discontiguous, both in time and space.
The keyword constexpr was introduced in C++11 and improved in C++14. It means constant expression. Like const , it can be applied to variables: A compiler error is raised when any code attempts to modify the value. Unlike const , constexpr can also be applied to functions and class constructors.
const can only be used with non-static member functions whereas constexpr can be used with member and non-member functions, even with constructors but with condition that argument and return type must be of literal types.
A const int var can be dynamically set to a value at runtime and once it is set to that value, it can no longer be changed. A constexpr int var cannot be dynamically set at runtime, but rather, at compile time. And once it is set to that value, it can no longer be changed.
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).
The behavior changed since C++17. Before C++17, even a constexpr static data member must be initialized inside the class definition, definition at namespace scope is still required; Since C++17 the namespace scope definition is not required again.
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)
Compiling your code with a compiler supporting C++17 would work fine.
LIVE demo with gcc7
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With