【问题标题】:Constant expression initializer for static class member of type doubledouble 类型的静态类成员的常量表达式初始值设定项
【发布时间】:2015-08-24 21:01:17
【问题描述】:

在C++11和C++14中,为什么在下面的sn-p中需要constexpr

class Foo {
    static constexpr double X = 0.75;
};

而这会产生编译器错误:

class Foo {
    static const double X = 0.75;
};

而且(更令人惊讶的是)编译没有错误?

class Foo {
    static const double X;
};

const double Foo::X = 0.75;

【问题讨论】:

  • 主要兼容C++03,here is a summary
  • 一种用于编译时常量的语法,与创建static 成员变量的语法不同,该成员变量是const,具有默认(因此是单数)值。也许这里的部分逻辑是 const 可以被违反,如果你说你希望能够获取事物的地址,那么你可能会遇到这种恶作剧。
  • @Stefano Sanfilippo:为什么最后一个例子被描述为“更令人惊讶”?它实际上是该语言从一开始就存在的基线行为。
  • @AnT 好吧,这就是我在下面引用的问题 1826 的内容,在 C++11 中人们发现这令人惊讶且不一致。

标签: c++ c++11 c++14 static-members constant-expression


【解决方案1】:

在 C++03 中,我们只允许为枚举类型的 const 积分的静态成员变量提供类内初始化器,在 C++11 中,我们可以使用 constexpr 在类中初始化字面量类型的静态成员。这个限制保留在 C++11 中用于 const 变量,主要是为了与 C++03 的兼容性,我们可以从 closed issue 1826: const floating-point in constant expressions 看到这一点:

用常量初始化的 const 整数可以在常量表达式中使用,但用常量初始化的 const 浮点变量不能。这是有意为之,与 C++03 兼容,同时鼓励一致使用 constexpr。然而,有些人发现这种区别令人惊讶。

CWG 最终以非缺陷(NAD)关闭了此请求,基本上是说:

希望浮点值参与常量表达式的程序员应该使用 constexpr 而不是 const。

供参考N1804 最接近 C++03 的标准草案在 9.4.2 部分公开提供 [class.static.data] 说:

如果静态数据成员是 const 整型或 const 枚举类型,它在类定义中的声明可以 指定一个常量初始化器,它应该是一个整数常量表达式(5.19)。在这种情况下,该成员可以出现 在整数常量表达式中。如果在程序中使用该成员,则该成员仍应在命名空间范围内定义,并且 命名空间范围定义不应包含初始化程序。

C++11 标准部分草案9.4.2[class.static.data] 说:

如果非易失性 const 静态数据成员是整型或枚举类型,则其在类中的声明 定义可以指定一个大括号或等式初始化器,其中每个初始化器子句都是一个赋值表达式 是一个常数表达式 (5.19)。文字类型的静态数据成员可以在 带有 constexpr 说明符的类定义;如果是这样,它的声明应指定一个大括号或相等初始化器 其中每个作为赋值表达式的初始化子句都是常量表达式。 [...]

这在 C++14 标准草案中几乎相同。

【讨论】:

    【解决方案2】:

    类内静态常量“定义”实际上是声明。定义变量时,编译器会为该变量分配内存,但这里不是这种情况,即获取这些类中静态常量的地址是错误的,NDR。

    这些东西应该是写进代码里的,但是浮点类型做起来不是那么容易,所以是不允许的。

    通过在类外部定义静态 const 变量,您向编译器发出信号,这是真实定义 - 具有内存位置的真实实例。

    【讨论】:

    • 它实际上是格式不正确的 NDR,而不是 UB。
    • "但是浮点类型不太容易做到,因此不允许" 在 C++98/03 中可能是这种情况,但是C++11 引入了constexpr,允许struct X { constexpr static double d = 5.0; };
    • @dyp 你是说浮点类型也加入了代码?!我认为这是一个“真正的定义”,即 constexpr double 存储在内存中的某个位置。
    • 不,具有类内初始化程序的类的静态数据成员不是定义,无论是 constconstexpr 还是两者。您可以在某些情况下使用它的 value(非 ODR 使用),但如果您需要,例如它的地址,仍然需要一个定义。 -- 也就是说constexpr不影响一个声明是否也是一个定义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    • 1970-01-01
    • 2012-02-18
    • 1970-01-01
    • 2013-08-31
    相关资源
    最近更新 更多