Reputation: 2823
Here is my working code example:
#include <iostream>
template<typename B>
class b {
public:
int y;
constexpr b(int x) : y(x) {
}
constexpr void sayhi() {
std::cout << "hi" << std::endl;
}
};
template<int x>
struct A {
static constexpr b<int> bee = x;
static constexpr int y = x; // this one is fine and usable already, I don't have to do something like what I did on member bee
inline static void sayhi() {
std::cout << y << std::endl;
}
};
template<int x>
constexpr b<int> A<x>::bee; // why do I have to do something like this, it will cause errors if I don't.
int main(int argc, char** argv) {
A<30>::bee.sayhi(); // works fine
A<30>::sayhi(); // works fine
return 0;
}
What my code does is simple, I have template struct A
that has two static variables, namely a static constexpr int y
and a static constexpr b<int> bee = x;
. My template struct A
will get the value of the argument which will be copied by x
from the template parameter. My question is: how come when it comes to classes, I have to initialize the class by doing something like this:
template<int x>
constexpr b<int> A<x>::bee;
If I don't use the code above, I get the undefined
reference error. Wherein the int is already fine and accessible just from doing something like:
static constexpr int y = x;
I am concerned why I don't have to forward declare it anymore.
Upvotes: 16
Views: 34885
Reputation: 802
Since C++ 17 introduced static inline variables and made static constexpr variables implicitly inline, the answer to your question is now much simpler. So in your example, you could simply use the following without any declaration of variable bee
outside the class (although it's still allowed for compatibility reasons):
template<int x>
struct A {
static constexpr b<int> bee = x;
};
Source: cppreference
A static data member may be declared inline. An inline static data member can be defined in the class definition and may specify an initializer. It does not need an out-of-class definition:
struct X { inline static int n = 1; };
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 [...]) is still permitted, but is deprecated.
Upvotes: 9
Reputation: 137810
A static constexpr
member has a value upon its initialization inside the class { }
scope, but it does not have a location in memory (an address) until it is defined outside the class { }
. The reason is that you may decide to include some or all of its specializations in a link library (e.g. .o
or .so
), or whether to give effectively-inline linkage to specializations by default.
The out-of-class definition is required if the address of the object is ever used, which implies that it must exist as a global variable. On the other hand, if you want the constexpr
member only to exist at compile time, prohibiting global storage allocation, then omitting the definition is a good choice.
By the way, it's not allowed to put the constexpr
specifier on a function that can never be evaluated as a constant expression, such as sayhi
which prints to std::cout
. This is a "no diagnostic required (NDR)" rule, meaning that the compiler might not complain now but the next compiler version might.
Upvotes: 17