Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

enum vs constexpr for actual static constants inside classes

Let me start by stating my intent. In the olden (C++) days, we would have code like:

class C { public:   enum {SOME_VALUE=27}; }; 

Then we could use SOME_VALUE throughout our code as a compile time constant and wherever the compiler would see C::SOME_VALUE, it would just insert the literal 27.

Now days, it is seems more acceptable to change that code to something like:

class C { public:   static constexpr int SOME_VALUE=27; }; 

This looks much cleaner, gives SOME_VALUE a well defined type and seems to be the preferred approach as of C++11. The (unforseen at least for me) problem is that this also causes scenarios where SOME_VALUE needs to be made external. That is, in some cpp file somewhere, we need to add:

constexpr int C::SOME_VALUE; // Now C::SOME_VALUE has external linkage 

The cases that cause this seem to be when const references to SOME_VALUE are used, which happens quite often in C++ Standard Library code (See the example at the bottom of this question). I am using gcc 4.7.2 as my compiler by the way.

Due to this dilemma, I am forced to revert back to defining SOME_VALUE as an enum (i.e., old school) in order to avoid having to add a definition to a cpp file for some, but not all of my static constexpr member variables. Isn't there some way to tell the compiler that constexpr int SOME_VALUE=27 means that SOME_VALUE should be treated only as a compile time constant and never an object with external linkage? If you see a const reference used with it, create a temporary. If you see its address taken, generate a compile time error if that's what's needed, because it's a compile time constant and nothing more.

Here is some seemingly benign sample code that causes us to need to add the definition for SOME_VALUE in a cpp file (once again, tested with gcc 4.7.2):

#include <vector>  class C { public:   static constexpr int SOME_VALUE=5; };  int main() {   std::vector<int> iv;    iv.push_back(C::SOME_VALUE); // Will cause an undefined reference error                                // at link time, because the compiler isn't smart                                // enough to treat C::SOME_VALUE as the literal 5                                // even though it's obvious at compile time } 

Adding the following line to the code at file scope will resolve the error:

constexpr int C::SOME_VALUE; 
like image 264
Michael Goldshteyn Avatar asked Apr 04 '14 16:04

Michael Goldshteyn


People also ask

Should constexpr be static?

So you should definitely use static constexpr in your example. However, there is one case where you wouldn't want to use static constexpr . Unless a constexpr declared object is either ODR-used or declared static , the compiler is free to not include it at all.

Should I use constexpr or const?

const applies for variables, and prevents them from being modified in your code. constexpr tells the compiler that this expression results in a compile time constant value, so it can be used in places like array lengths, assigning to const variables, etc.

Is enum a constexpr?

There will be no noticeable difference for integral constants when used like this. However, enum is actually better, because it is a true named constant. constexpr integral constant is an object which can be, for example, ODR-used - and that would result in linking errors.

Is constexpr implicitly static?

constexpr functions are implicitly inline , but not implicitly static .


1 Answers

For the record, the static constexpr version will work like you'd expected in C++17. From N4618 Annex D.1 [depr.static_constexpr]:

D.1 Redeclaration of static constexpr data members [depr.static_constexpr]

For compatibility with prior C++ International Standards, a constexpr static data member may be redundantly redeclared outside the class with no initializer. This usage is deprecated. [Example:

struct A {  static constexpr int n = 5; // definition (declaration in C++ 2014) };  constexpr int A::n; // redundant declaration (definition in C++ 2014) 

end example]

The relevant standard text that allows this is N4618 9.2.3 [class.static.data]/3:

[...] An inline static data member may be defined in the class definition and may specify a brace-or-equal-initializer. If the member is declared with the constexpr specifier, it may be redeclared in namespace scope with no initializer (this usage is deprecated; see D.1). [...]

This comes with the same machinery that introduced the non-constexpr version of the same thing, inline static data members.

struct A {  static inline int n = 5; // definition (illegal in C++ 2014) };   inline int A::n; // illegal 
like image 168
TBBle Avatar answered Oct 02 '22 21:10

TBBle