Think about what constexpr
means. It means that I can resolve this value at compile time.
Thus, a member variable of a class cannot itself be a constexpr
...the instance that xVal
belongs to does not exist until instantiation time! The thing that owns xVal
could be constexp
, and that would make xVal
a constexpr
, but xVal
could never be constexpr
on its own.
That does not mean that these values can't be const expression...in fact, a constexpr instance of the class can use the variables as const expressions:
struct S {
constexpr S(int x, int y): xVal(x), yVal(y) {}
constexpr S(int x): xVal(x) {}
constexpr S() {}
int xVal { 0 };
int yVal { 0 };
};
constexpr S s;
template <int f>//requires a constexpr
int foo() {return f;}
int main()
{
cout << "Hello World" << foo<s.xVal>( )<< endl;
return 0;
}
Edit: So there has been alot of discussion below that reviewed that there was a couple of implied questions here.
"why can't I enforce all instances of a class to be constexpr by declaring its members to be constexpr?"
Take the following example:
//a.h
struct S;
struct A {std::unique_ptr<S> x; void Foo(); A();/*assume A() tries to instantiate an x*/}
//main.cpp
int main(int argc, char** argv) {
A a;
a->foo();
}
//S.h
struct S {
constexpr S(int x, int y): xVal(x), yVal(y) {}
constexpr S(int x): xVal(x) {}
constexpr S() {}
constexpr int xVal { 0 }; // error!
constexpr int yVal { 0 };
};
The definition of A and S could be in completely different compilation units, so the fact that S must be constexpr may not be known until link time, especially if the implementation of A is forgotten. Such ambiguous cases would be hard to debug and hard to implement. Whats worse is that the interface for S could be exposed entirely in a shared library, COM interface, ect...This could entirely change all the infrastructures for a shared library and that would probably be unacceptable.
Another reason would be how infectious that is. If any of the members of a class were constexpr, all the members (and all their members) and all instances would have to be constexpr. Take the following scenario:
//S.h
struct S {
constexpr S(int x, int y): xVal(x), yVal(y) {}
constexpr S(int x): xVal(x) {}
constexpr S() {}
constexpr int xVal { 0 }; // error!
int yVal { 0 };
};
Any instance of S would have to be constexpr
to be able to hold an exclusively constexpr
xval
. yVal
inherently becomes constexpr
because xVal
is. There is no technical compiler reason you can't do that (i don't think) but it does not feel very C++-like.
"OK, but i REEAAALLLY want to make all instances of a class constexpr. What is the technical limitation that prevents me from doing that".
Probably nothing other than the standards committee didn't think it was a good idea. Personally, I find it having very little utility...I don't really want to define how people use my class, just define how my class behaves when they use it. When they use it, they can declare specific instances as constexpr (as above). If I have some block of code that I would like a constexpr instance over, I'd do it with a template:
template <S s>
function int bar(){return s.xVal;}
int main()
{
cout << "Hello World" << foo<bar<s>()>( )<< endl;
return 0;
}
Though I think you'd be better off with a constexpr function that could be used both in the restrictive an non restrictive ways?
constexpr int bar(S s) { return s.xVal; }