Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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;
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
504 views
Welcome To Ask or Share your Answers For Others

1 Answer

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...