枚举与constexpr实际静态常量内类

首先说明我的意图。 在古代(C ++)的日子里,我们会有这样的代码:

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

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

现在,将代码更改为如下所示似乎更可接受:

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

这看起来更干净,给SOME_VALUE一个明确的types,似乎是C ++ 11的首选方法。 (至less对我而言)问题在于,这也会导致需要将SOME_VALUE设置为外部的场景。 也就是说,在某个cpp文件中,我们需要添加:

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

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

由于这个困境,我不得不回到定义SOME_VALUE作为一个枚举(即旧学校),以避免必须添加一个定义到一个cpp文件的一些,但不是我所有的静态constexpr成员variables。 是不是有一些方法来告诉编译器: constexpr int SOME_VALUE=27意味着SOME_VALUE 只能被视为编译时间常量而不会被外部连接的对象? 如果您看到使用它的const引用,请创build一个临时的。 如果你看到它的地址,那么就会产生一个编译时错误,如果这是所需要的,因为它是一个编译时间常数,而不是更多。

以下是一些看似良性的示例代码,它使我们需要在cpp文件中添加SOME_VALUE的定义(再次使用gcc 4.7.2进行testing):

 #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 } 

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

 constexpr int C::SOME_VALUE; 

为了logging, 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; // definition (declaration in C++ 2014) }; constexpr int A::n; // redundant declaration (definition in C++ 2014) 

结束示例 ]

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

[…]内联静态数据成员可以在类定义中定义,并可以指定一个括号或等于初始值设定项 。 如果成员是用constexpr声明的,那么它可以在没有初始化的情况下在名称空间范围内重新声明(这个用法已经被弃用;见D.1)。 […]

这与引入同一事物的非constexpr版本的相同机制, 内联静态数据成员

 struct A { static inline int n = 5; // definition (illegal in C++ 2014) }; inline int A::n; // illegal 

你有三个select:

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

  2. 如果你的类是非模板的,你可以很容易地把它放在源文件中

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

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

我会去与枚举类:

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

http://www.stroustrup.com/C++11FAQ.html#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 class : int C { SOME_VALUE = 5 }; 

从C ++标准N3797 S3.5 / 2-3

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

– 当名称有外部链接时,它所指的实体可以用其他翻译单位的名称或同一翻译单位的其他范围的名称来引用。

– 当一个名称有内部连接时,它所表示的实体可以被同一翻译单元中其他作用域的名称引用。

– 当名称没有链接时,它所指的实体不能被其他作用域的名称引用。

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

– 显式声明为静态的variables,函数或函数模板; 要么,

– 显式声明为const或constexpr的非易失性variables,既不明确声明为extern,也不声明为具有外部链接; 要么

– 匿名工会的数据成员。

我的阅读是在下面的代码中:

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

SOME_VALUE所有4个实例都有内部链接。 他们应该在同一个翻译单元中与SOME_VALUE的引用链接,而不是在别处可见。

显然,第一个是一个声明,而不是一个定义。 它需要在同一翻译单位内定义。 如果海湾合作委员会这样说,而MSVC不,那么MSVC是错误的。

为了replace枚举的目的,数字2应该工作正常。 它仍然有内部联系,没有static关键字。

[回应评论编辑]

你可以这样做

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

这甚至不是C ++ 11,只是C ++ 98