Enum vs constexpr for real static constants inside classes

Let me begin by declaring my intention. In the old (C ++) days, we would have code like:

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

Then we could use SOME_VALUE in all of our code as a compile-time constant, and wherever the compiler sees C::SOME_VALUE , it simply inserts the literal 27.

Now, it seems more acceptable to change this code to something like:

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

It looks a lot cleaner, gives SOME_VALUE well-defined type, and seems to be the preferred approach with C ++ 11. The problem (unforeseen, at least for me) is that it also causes scripts in which SOME_VALUE needed make external. That is, in some cpp file we need to add:

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

Cases that lead to this occur when SOME_VALUE links are SOME_VALUE , which is quite common in the code of the C ++ standard library (see the example at the bottom of this question). By the way, I use gcc 4.7.2 as my compiler.

Due to this dilemma, I have to revert to defining SOME_VALUE as an enum (i.e. old school) to avoid having to add a definition to the cpp file for some, but not all of my static constexpr member variables. Is there no way to tell the compiler that constexpr int SOME_VALUE=27 means that SOME_VALUE should be considered only as a compile-time constant and never an object with external connection? If you see a link to the constant used with it, create a temporary one. If you see its address, generate a compile-time error, if necessary, because it is a compile-time constant and nothing else.

Here is some seemingly benign code sample that forces us to add a definition for SOME_VALUE to the cpp file (once again, checked 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 obvious at compile time } 

Adding the following line to the code in the file area will result in an error:

 constexpr int C::SOME_VALUE; 
+57
c ++ c ++ 11 constexpr
Apr 04 '14 at 16:02
source share
6 answers

For the record, the static constexpr version will work as you expected in C ++ 17. From N4618 Appendix D.1 [des.static_constexpr] :

D.1 Updating static constexpr data static constexpr [des.static_constexpr]

For compatibility with previous C ++ standards, the constexpr static data constexpr may be overridden override outside the class without an 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]

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

[...] An inline static data element can be defined in a class definition and can indicate a boolean or peer-initializer. If a member is declared with type constexpr , it can be updated in the namespace scope without an initializer (this use is deprecated, see D.1). [...]

This is due to the same mechanism that introduced the constexpr version of the same thing, built-in static data elements .

 struct A { static inline int n = 5; // definition (illegal in C++ 2014) }; inline int A::n; // illegal 
+3
Apr 25 '17 at 22:17
source share

You have three options:

  • If your class is a template, then put the definition of the static member in the header itself. The compiler must identify it as one definition for only a few translation units (see [Basic.def.odr] / 5)

  • If your class is not a template, you can easily put it in the source file

  • Alternatively declare a const member function constexpr getSomeValue ():

     class C { public: static constexpr int getSomeValue() { return 27; } }; 
+7
Apr 27 '14 at 4:00
source share

I would go with the enum class:

http://en.cppreference.com/w/cpp/language/enum

http://www.stroustrup.com/C++11FAQ.html#enum

From the first link:

 enum class Color { RED, GREEN=20, BLUE}; Color r = Color::BLUE; switch(r) { case Color::RED : std::cout << "red\n"; break; case Color::GREEN : std::cout << "green\n"; break; case Color::BLUE : std::cout << "blue\n"; break; } // int n = r; // error: no scoped enum to int conversion int n = static_cast<int>(r); // OK, n = 21 
+3
Apr 28 '14 at 18:53
source share

From C ++ N3797 S3.5 / 2-3 standard

It is said that a name has a relationship when it can denote the same object, link, function, type, template, namespace or value as the name entered by the declaration in another area:

- If the name has an external reference, the object that it denotes can be called names from the areas of other translation units or from other areas of the same translation unit.

- If the name has an internal connection, the entity that it denotes can refer to names from other areas in the same translation unit.

- If the name does not have a binding, the object that it designates cannot refer to names from other areas.

A name that has a namespace scope (3.3.6) has an internal relationship if that name

- a template for a variable, function, or function that is explicitly declared static; or,

- a non-volatile variable explicitly declared as const or constexpr, and none of the explicitly declared extern or previously not declared to have an external connection; or

is a data member of an anonymous union.

My reading is that in the following code:

 public: static constexpr int SOME_VALUE=5; constexpr int SOME_VALUE=5; }; static constexpr int SOME_VALUE=5; constexpr int SOME_VALUE=5; 

All 4 SOME_VALUE instances are internally linked. They must refer to SOME_VALUE in the same translation unit and not be visible elsewhere.

Obviously, the first is a declaration, not a definition. It needs to be defined within one translation unit. If GCC talks about it, but MSVC doesn't, then MSVC is wrong.

In order to replace the listing, number 2 should work fine. It still has an internal link without the static .

[Edited in response to the comment]

+1
Apr 15 '14 at 2:56
source share

Currently preferred method:

 enum class : int C { SOME_VALUE = 5 }; 
+1
Apr 15 '14 at 16:50
source share

You can do it

 class C { public: static const int SOME_VALUE=5; }; int main() { std::vector<int> iv; iv.push_back(C::SOME_VALUE); } 

It is not even C ++ 11, just C ++ 98

-one
Apr 07 '14 at 16:21
source share



All Articles