enum vs constexpr用于类中的实际静态常量

Mic*_*eyn 66 c++ constexpr c++11

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

class C
{
public:
  enum {SOME_VALUE=27};
};
Run Code Online (Sandbox Code Playgroud)

然后我们可以SOME_VALUE在整个代码中使用编译时常量,无论编译器在哪里看C::SOME_VALUE,它都只是插入文字27.

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

class C
{
public:
  static constexpr int SOME_VALUE=27;
};
Run Code Online (Sandbox Code Playgroud)

这看起来更清晰,提供SOME_VALUE了一个定义良好的类型,并且似乎是C++ 11中的首选方法.(至少对我来说不可靠)问题是,这也导致SOME_VALUE需要在外部进行的情况.也就是说,在某个地方的某个cpp文件中,我们需要添加:

constexpr int C::SOME_VALUE; // Now C::SOME_VALUE has external linkage
Run Code Online (Sandbox Code Playgroud)

导致这种情况的情况似乎是在使用const引用时SOME_VALUE,这在C++标准库代码中经常发生(请参阅本问题底部的示例).顺便说一句,我使用gcc 4.7.2作为我的编译器.

由于这种困境,我被迫恢复定义SOME_VALUE为枚举(即旧学校),以避免必须为某些(但不是所有)静态constexpr成员变量的cpp文件添加定义.是不是有某种方法告诉编译器这constexpr int SOME_VALUE=27意味着SOME_VALUE应该将其视为编译时常量而不是具有外部链接的对象?如果您看到与它一起使用的const引用,请创建一个临时引用.如果你看到它的地址,如果需要的话就会产生编译时错误,因为它是编译时常量而已.

以下是一些看似良性的示例代码,它们使我们需要SOME_VALUE在cpp文件中添加定义(再次使用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
}
Run Code Online (Sandbox Code Playgroud)

将以下行添加到文件范围的代码将解决该错误:

constexpr int C::SOME_VALUE;
Run Code Online (Sandbox Code Playgroud)

apo*_*kul 9

这里有三个选项:

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

  2. 如果您的类是非模板,则可以轻松将其放入源文件中

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

    class C
    {
    public:
        static constexpr int getSomeValue() { return 27; }
    };
    
    Run Code Online (Sandbox Code Playgroud)


TBB*_*Ble 8

对于记录,该static constexpr版本将像您在C++ 17中所期望的那样工作.来自N4618附件D.1 [depr.static_constexpr]:

D.1 static constexpr数据成员的重新声明[depr.static_constexpr]

为了与先前的C++国际标准兼容,constexpr可以在类外部冗余地重新声明静态数据成员而不使用初始化程序.不推荐使用此用法.[ 例如:

struct A {
 static constexpr int n = 5; // de?nition (declaration in C++ 2014)
};

constexpr int A::n; // redundant declaration (de?nition in C++ 2014)
Run Code Online (Sandbox Code Playgroud)

- 结束例子 ]

允许这样做的相关标准文本是N4618 9.2.3 [class.static.data]/3:

[...]内联静态数据成员可以在类定义中定义,并且可以指定括号或等于初始化器.如果使用constexpr指定者声明成员,则可以在没有初始化程序的命名空间作用域中重新声明该成员(此用法已弃用;请参阅D.1).[...]

这与引入相同内容的非constexpr版本的内联静态数据成员的机器相同.

struct A {
 static inline int n = 5; // de?nition (illegal in C++ 2014)
}; 

inline int A::n; // illegal
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

12424 次

最近记录:

8 年,6 月 前