C++ 类内实际静态常量的enum vs constexpr

C++ 类内实际静态常量的enum vs constexpr,c++,c++11,constexpr,C++,C++11,Constexpr,让我先说明我的意图。在旧的(C++)时代,我们会有如下代码: class C { public: enum {SOME_VALUE=27}; }; 然后我们可以在整个代码中使用SOME\u VALUE作为编译时常量,无论编译器在哪里看到C::SOME\u VALUE,它都会插入文本27 现在,将代码更改为以下内容似乎更容易接受: class C { public: static constexpr int SOME_VALUE=27; }; 这看起来更简洁,为提供了一个定义良好的类型

让我先说明我的意图。在旧的(C++)时代,我们会有如下代码:

class C
{
public:
  enum {SOME_VALUE=27};
};
然后我们可以在整个代码中使用
SOME\u VALUE
作为编译时常量,无论编译器在哪里看到
C::SOME\u VALUE
,它都会插入文本27

现在,将代码更改为以下内容似乎更容易接受:

class C
{
public:
  static constexpr int SOME_VALUE=27;
};
这看起来更简洁,为
提供了一个定义良好的类型,似乎是C++11以后的首选方法。(至少对我来说,这是不可预见的)问题是,这也会导致出现需要将某个值设置为外部值的情况。也就是说,在某个cpp文件中,我们需要添加:

constexpr int C::SOME_VALUE; // Now C::SOME_VALUE has external linkage
<> P> >当C++引用标准库代码时,经常出现这种情况,这是经常出现在C++代码库代码中的(参见这个问题的底部的例子)。顺便说一下,我正在使用GCC4.7.2作为我的编译器

由于这一困境,我不得不重新将
一些_值
定义为一个枚举(即,旧的学校),以避免为一些但不是所有静态constepr成员变量向cpp文件添加定义。难道没有办法告诉编译器,
constexpr int some_VALUE=27
意味着
some_VALUE
只应被视为编译时常量,而不应被视为具有外部链接的对象吗?如果看到与之一起使用的常量引用,请创建一个临时引用。如果您看到它的地址被占用,如果需要的话,生成一个编译时错误,因为它是一个编译时常量,仅此而已

下面是一些看似良性的示例代码,导致我们需要在cpp文件中添加
某些值的定义(再次使用gcc 4.7.2进行测试):

你可以这样做

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

int main()
{
  std::vector<int> iv;
  iv.push_back(C::SOME_VALUE); 
}
C类
{
公众:
静态常数int一些_值=5;
};
int main()
{
std::载体iv;
iv.推回(C::某些值);
}

<>这不是C++ 11,只是C++ C++ 98标准中的N397 S3.5/2-3

当一个名称可能表示与另一个作用域中的声明引入的名称相同的对象、引用、函数、类型、模板、命名空间或值时,称该名称具有链接:

-当名称具有外部链接时,它所表示的实体可以通过来自其他翻译单元的范围或来自同一翻译单元的其他范围的名称来引用

-当名称具有内部链接时,它所表示的实体可以由同一翻译单元中其他作用域的名称引用

-当一个名称没有链接时,它所表示的实体不能被其他作用域中的名称引用

具有命名空间范围(3.3.6)的名称如果是的名称,则具有内部链接

-明确声明为静态的变量、函数或函数模板;或者

-非易失性变量,显式声明为const或constexpr,既没有显式声明为extern,也没有先前声明为具有外部链接;或

-匿名联盟的数据成员

我的理解是,在以下代码中:

public:
  static constexpr int SOME_VALUE=5;
  constexpr int SOME_VALUE=5;
};
static constexpr int SOME_VALUE=5;
constexpr int SOME_VALUE=5;
某些值的所有4个实例都有内部链接。它们应该链接到同一翻译单元中的某个值,并且在其他地方不可见

显然,第一个是声明,而不是定义。它需要在同一翻译单元中定义。如果GCC这么说而MSVC没有,那么MSVC是错的

为了替换枚举,数字2应该可以正常工作。它仍然有内部链接,没有
静态
关键字


[根据评论编辑]

如今,首选的方式是:

enum class : int C { SOME_VALUE = 5 };

这里有三个选项:

  • 如果您的类是template,那么将静态成员的定义放在头本身中。编译器需要在多个翻译单元中将其标识为一个定义(请参见[basic.def.odr]/5)

  • 若你们的类是非模板类,你们可以很容易地把它放在源文件中

  • 或者声明constexpr静态成员函数getSomeValue():


  • 我会选择enum类:

    从第一个链接:

    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
    
    enum类颜色{红色,绿色=20,蓝色};
    颜色r=颜色::蓝色;
    开关(r){
    
    案例颜色:红色:std::cout对于记录,
    静态constexpr
    版本将像您在C++17中预期的那样工作。从N4618附录d.1:

    D.1重新声明
    静态constexpr
    数据成员[depr.static\u constexpr]

    与C++以前的国际标准兼容,一个<代码> CONTXPRP</COD>静态数据成员可以在没有初始化器的情况下被冗余地声明在该类之外。 -[结束示例]

    允许此操作的相关标准文本为N4618 9.2.3:

    […]可以在类定义中定义内联静态数据成员,并可以指定大括号或相等的初始值设定项。如果使用
    constexpr
    说明符声明该成员,则可以在命名空间范围中重新声明该成员,而不使用初始值设定项(不推荐使用此用法;请参见D.1)。[…]

    这与引入同一事物的非
    constexpr
    版本的机制相同


    “当获取
    SOME_值的地址时,….我被迫返回到将
    SOME_值定义为
    enum
    ”,这让我非常困惑。枚举数是prvalues,您也不能获取它们的地址。您可以使用
    static constexpr int SOME_值(){return 5;}
    …顺便说一句,您现在可以给枚举数一个定义良好的类型:
    enum:int{SOME_VALUE=5};
    从类似的问题和答案中,例如],],],我收集了许多解决方法。最简洁的方法是使用
    +SOME_VALUE
    来获得临时值。所有这些解决方法都向我表明,使用
    enum
    enum class : int C { SOME_VALUE = 5 };
    
    class C
    {
    public:
        static constexpr int getSomeValue() { return 27; }
    };
    
    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
    
    struct A {
     static constexpr int n = 5; // definition (declaration in C++ 2014)
    };
    
    constexpr int A::n; // redundant declaration (definition in C++ 2014)
    
    struct A {
     static inline int n = 5; // definition (illegal in C++ 2014)
    }; 
    
    inline int A::n; // illegal