57

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?

KnowItAllWannabe
  • 12,972
  • 8
  • 50
  • 91
  • 23
    Because they are member variables that are initialized at runtime and cannot be evaluated until they are initialized. – Captain Obvlious Dec 10 '14 at 19:08
  • @remyabel I believe why I can't have a constexpr is different than why I am I getting constexpr errors when I don't use brace initializes... – IdeaHat Dec 10 '14 at 19:16
  • @IdeaHat Have you read the answer? –  Dec 10 '14 at 19:18
  • @remyabel Yup. Says you have to use brace initializer, which KnowItAllWannabe did. – IdeaHat Dec 10 '14 at 19:19
  • 1
    @remyabel: This question is completely different. It has nothing to do with initializing a static data member. Is there a way to get the misleading link at the top of my question to go away? – KnowItAllWannabe Dec 10 '14 at 20:24
  • 1
    @KnowItAllWannabe I flagged it as not constructive, the comment vacuum will come and blow all the comments away eventually. – IdeaHat Dec 10 '14 at 20:28

1 Answers1

56

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; }
IdeaHat
  • 7,641
  • 1
  • 22
  • 53
  • I'd expect that constexpr data members would yield a class where all instances could be declared constexpr. That is, attempts to create objects with non-constexpr values would be rejected during compilation. – KnowItAllWannabe Dec 10 '14 at 19:32
  • @KnowItAllWannabe constexpr isn't a restriction on how something can be used; rather it is a permission to use it in places where constexpr is only allowed to be used. A constexpr function given non-constexpr arguments will run at runtime and not compile time...this is a good thing, because I don't want to duplicate my code for each case (like I used to have to do). Any class that could be instantiated as constexpr can always have instances that are not constexpr (All I'd need to do is call new S). Does that make sense? – IdeaHat Dec 10 '14 at 19:39
  • With mutable data members of objects declared as `constexpr`, it is even arguable that `constexpr` means *value known at compile-time*. `constexpr` on types is indeed a restriction; one could imagine `constexpr` data members requiring initialization with a constant expression. Maybe, the benefits are too small or the original intention of `constexpr` violated for non-static data members. – dyp Dec 10 '14 at 20:10
  • @IdeaHat: It makes sense, and I understand that, but I think it also makes sense to want to ensure that all instances of a class have values known during compilation. – KnowItAllWannabe Dec 10 '14 at 20:12
  • @KnowItAllWannabe I don't believe that is possible, and, to /u/dyp's point, I don't see the utility. Requiring all objects to be constexpr feels very much like all the things wrong with the singleton pattern...it closes me, as a user of the class, off from doing many things in order to provide a feature to me that would be trivial for me to provide on a case-by-case basis. – IdeaHat Dec 10 '14 at 20:17
  • @IdeaHat: You could say the same thing about final: it prevents you from doing something you might want to do. Anyway, I don't see the relationship with Singleton: each object with constexpr data members could have a different set of values. – KnowItAllWannabe Dec 10 '14 at 20:22
  • @KnowItAllWannabe Right, but I could never put it in a dynamic array, never use it as a mutable, never re-factor the code to configure from a settings file. BTW, the alternative is that you can make a specific instances constexpr as in my example. You can make functions take in an template argument of the class type to enforce receiving a constexpr. (though I would think you'd be better off in the second case to provide a constexpr function that could be used either way). – IdeaHat Dec 10 '14 at 20:25
  • 1
    @IdeaHat: But now you're arguing that non-static constexpr data members would be bad design, whereas your proposed answer seems to argue that the values of such data members wouldn't be determinable during compilation. (Your example demonstrates that their values can be, provided they're initialized with constexpr values.) My fundamental interest is in knowing if there is some technical constraint that would make non-static constexpr data members unimplementable. – KnowItAllWannabe Dec 10 '14 at 20:32
  • @KnowItAllWannabe Which I answered in the question (non-static constexpr data members are unimplementable because they inherit their constexpr-ness from their instance). You now have a new question, which is "Why can't I make all instances generated by a type constexpr", which if you made, I would totally follow to see if there was actually a way :-P – IdeaHat Dec 10 '14 at 20:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/66607/discussion-between-ideahat-and-knowitallwannabe). – IdeaHat Dec 10 '14 at 20:42
  • @IdeaHat: I don't find it a convincing explanation, because one could say the same thing about const. Yet a non-static data member can be declared const independent of whether its containing instance is const. – KnowItAllWannabe Dec 10 '14 at 20:42
  • @KnowItAllWannabe completely different things (in fact, in c++14 constexpr does not imply const). A const value can still be const if I initiate it on the heap, for example. constexpr would thus be parasitic to the containing class in ways that const would not be...if any member of the class was constexpr, the owning class and all members would also have to be constexpr. – IdeaHat Dec 10 '14 at 20:48
  • @KnowItAllWannabe Again a different (and good) question than the one you asked, would be "why can't const constexpr be allowed to implicitly infect my code", which would be technically difficult in C++ because you could pre-declare a class and use it as a pointer before defining the methods...not impossible in my mind, but I'm not a compiler expert. As it stands, constexpr is not infectious is the answer to "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?" – IdeaHat Dec 10 '14 at 20:57
  • @IdeaHat: I don't know what you mean by "infectious" here, but I'm not sure we'll make much headway by further discussion. I appreciate and I believe I understand your perspective, but I don't find it compelling. – KnowItAllWannabe Dec 10 '14 at 21:04
  • infectious pure `constexpr` exists and is called `consteval` – v.oddou Apr 01 '19 at 08:14
  • Why can't `static` and `constexpr` be mutually inclusive? In other words, if I define a `constexpr` data member in a class, why can it not be implied `static` storage duration? Compiler won't let you declare `constexpr` without it anyway, so it makes sense for the declaration to be less redundant, imply `static`, and then work. I think C++17's rules on `inline` variables makes this more confusing/mysterious. Thoughts? – void.pointer Jul 15 '19 at 17:48
  • @void.pointer `static constexpr` is absolutely valid and used. I didn't think that is what the original author was going for, but maybe that is true? I'd suggest https://stackoverflow.com/questions/34053606/understanding-static-constexpr-member-variables for more info on that topic. – IdeaHat Jul 15 '19 at 20:02