This is valid code:
struct S { constexpr S(int x, int y): xVal(x), yVal(y) {} constexpr S(int x): xVal(x) {} constexpr S() {} const int xVal { 0 }; const int yVal { 0 }; };
But here I'd really like to declare xVal
and yVal
constexpr
--like this:
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 }; // error! };
As indicated, the code won't compile. The reason is that (per 7.1.5/1), only static data members may be declared constexpr
. But why?
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.
constexpr functions are implicitly inline , but not implicitly static .
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.
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.
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.
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.
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; }
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