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.
A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later. So, what does constexpr mean?
constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.
The alternatives don't have the all of the positives of static constexpr - you're guaranteed compile time processing, type safety, and (potentially) lower usage of memory (constexpr variables don't need to take up memory, they are effectively hard coded unless if possible).
Constant Expression Constructors A constexpr constructor allows the compiler to initialize the object at compile-time, provided that the constructor's arguments are all constant expressions. Formally, a constant expression constructor is one that meets the following criteria: It's declared constexpr explicitly.
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.
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