Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't non-static data members be constexpr?

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?

like image 728
KnowItAllWannabe Avatar asked Dec 10 '14 19:12

KnowItAllWannabe


People also ask

Why does constexpr need to be static?

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.

Is constexpr implicitly static?

constexpr functions are implicitly inline , but not implicitly static .

Can a member function be constexpr?

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.

What can be constexpr?

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.


1 Answers

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; } 
like image 63
IdeaHat Avatar answered Oct 07 '22 21:10

IdeaHat