【问题标题】:Why must non-integral static data members initialized in the class be constexpr?为什么在类中初始化的非整数静态数据成员必须是 constexpr?
【发布时间】:2013-10-24 19:49:57
【问题描述】:

类定义中初始化的静态整型数据成员可以声明constconstexpr,但类定义中初始化的非整型静态数据成员必须是constexpr

class MyClass {
  static const     int   w = 5;          // okay
  static constexpr int   x = 5;          // okay
  static const     float y = 1.5;        // error!
  static constexpr float z = 1.5;        // okay
};

有人知道为什么不允许声明 y 吗?使其非法的标准部分是 9.4.2/3,但为什么它是非法的?

【问题讨论】:

  • 历史、遗产、演变、传统?
  • 你看过this answer吗?
  • @GabrielL.:您链接到的答案似乎是涉及 C++98 的讨论,而不是 C++11。

标签: c++ c++11 static-members constexpr


【解决方案1】:

在 C++11 之前,您无法在类声明中初始化非整数/枚举类型的静态成员(但您可以在类声明之外)。管理 constexpr 的规则延续了这一点,但允许您在类声明中使用 constexpr 对其进行初始化(因此您不再需要如下代码):

struct A
{
    static const float pi;
};

const float A::pi = 3.1415;

这条规则的一个副作用是简化你的类结构而不是让它变得丑陋(就像上面的代码)。

在 C++11 添加 constexpr 之前出现这种情况的原因之一是标准没有指定如何实现浮点(它留给处理器/架构 - 例如,当你说float x = 1.6f,实际上在大多数系统上是1.6000000000024)。

【讨论】:

  • 你的想法是对的,但是一个坏例子。 1.5f 可以精确地表示为二进制或十进制浮点类型,并且在几乎任何系统上都应该精确地存储为 1.5。
  • @hvd 重点是你不能依赖它,因为它依赖于平台。
  • 您写道“在大多数系统上实际上是 1.499999999999999”,这不仅仅是说它不需要精确存储。如果你发现任何系统没有准确存储它,我会感到非常惊讶,即使你是对的,它是允许的。
  • @hvd 如果它让你感觉更好,请用 1.6f 和 1.600000024 替换它。不过,重点是一样的。
  • 这就是我在第一条评论中试图指出的,你的观点是有效的,这只是一个例子,并没有(在典型的实现上)说明这一点。你现在提到的 1.6f 可以。
【解决方案2】:

float 的动机有点难以描述,但想象一下班级成员:

class MySpecialInt {
public:
    constexpr MySpecialInt(const int & other) {
    }
};
class MyClass {
    static const     MySpecialInt a = 5; // error
    static constexpr MySpecialInt b = 5; // okay
};

a 在这种情况下可能有一些可能违反(或至少严重复杂化)单一定义规则的重要构造。因为constexpr 保证了限制性的编译时属性,所以b 的复制构造函数必须 也是constexpr,因此保证在编译时返回一个明确定义的值(并且违反单一定义规则)

为什么float 表现出这种行为我认为只是出于遗留原因,因为float 传统上从未像这样初始化(“因为标准是这样说的”),所以他们抓住了初始化static const floatconstexpr旗下成员。

【讨论】:

  • 我有兴趣了解有关 ODR 如何对此进行反馈的更多详细信息。 ab 中的每一个都必须只初始化一次。这意味着必须只调用一次适当的构造函数。对于a,我们不知道是在编译期间还是在运行时进行构造,但在什么情况下abMySpecialInt构造函数可以被定义多次(例如这是由于使用了constexpr 而不是const)?
  • 它们实际上在每个翻译单元中定义一次。所以如果你在两个位置包含这个类,ab 被定义了两次,但是因为bconstexpr 它保证始终是相同的值,所以它是合法的。 a 可能有一些内部计数器或其他逻辑会导致 a 在翻译单元之间有所不同,这就是为什么将其声明为 static const 是非法的。
  • 根据 9.4.2/3,带有初始值设定项的静态数据成员的声明不是定义,如果这些成员被 odr 使用,则需要在类之外定义这些成员。但是您提到的问题(如果存在)同样适用于const--而不是constexpr--动态初始化的整数类型的成员。为产生初始化值而调用的函数可以在不同的调用中返回不同的值。所以我不明白这如何解释非整数静态数据成员的限制。
  • 在同一节中,它指出整数类型的初始化子句必须都是常量表达式,因此它们本质上不能动态初始化。
  • 好的,但是同样的限制适用于 const 静态数据成员,所以如果 a 的声明是合法的,MySpecialInt 的构造函数仍然必须是 constexpr。如果是这样的话,仍然不清楚(至少对我来说)为什么constexpr 可以,但const 不行。我一定还是错过了什么。
【解决方案3】:

这可能是因为非整数 i 也可能包含像 char 这样的数据类型,这就是为什么你不能将它们设为常量并需要常量表达式。但在整数的情况下,你可以将它们设为常量表达式或持续的。所以,因为 char 只能是常量表达式,所以对于所有非整数值都是非法的。

【讨论】:

    猜你喜欢
    • 2013-10-19
    • 1970-01-01
    • 2016-10-15
    • 1970-01-01
    • 1970-01-01
    • 2013-01-10
    • 2018-11-11
    • 2012-07-16
    相关资源
    最近更新 更多