enum vs constexpr for actual static constants inside classes

我们两清 提交于 2019-11-27 11:11:06
TBBle

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

You have three options here:

  1. If your class is template, then put the definition of static member in header itself. Compiler is required to identify it as one definition only across multiple translation units (see [basic.def.odr]/5)

  2. If your class is non-template you can easily put it in source file

  3. Alternatively declare constexpr static member function getSomeValue():

    class C
    {
    public:
        static constexpr int getSomeValue() { return 27; }
    };
    

I'd go with 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

Nowadays, the preferred way is:

enum class : int C { SOME_VALUE = 5 };

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

A name is said to have linkage when it might denote the same object, reference, function, type, template, namespace or value as a name introduced by a declaration in another scope:

— When a name has external linkage , the entity it denotes can be referred to by names from scopes of other translation units or from other scopes of the same translation unit.

— When a name has internal linkage , the entity it denotes can be referred to by names from other scopes in the same translation unit.

— When a name has no linkage , the entity it denotes cannot be referred to by names from other scopes.

A name having namespace scope (3.3.6) has internal linkage if it is the name of

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

— a non-volatile variable that is explicitly declared const or constexpr and neither explicitly declared extern nor previously declared to have external linkage; or

— 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 instances of SOME_VALUE have internal linkage. They should link with a reference to SOME_VALUE in the same translation unit and not be visible elsewhere.

Obviously the first one is a declaration and not a definition. It needs a definition within the same translation unit. If GCC says so and MSVC does not, then MSVC is wrong.

For the purposes of replacing an enum, number 2 should work fine. It still has internal linkage without the static keyword.

[Edited in response to comment]

you can do this

class C
{
public:
  static const int SOME_VALUE=5;
};

int main()
{
  std::vector<int> iv;
  iv.push_back(C::SOME_VALUE); 
}

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!