【问题标题】:In class nested static const member variable initialization Clang vs GCC which compiler is right?在类中嵌套静态 const 成员变量初始化 Clang vs GCC 哪个编译器是正确的?
【发布时间】:2016-03-04 09:55:11
【问题描述】:

考虑以下代码:

#include <iostream>

struct Foo {
  static int const i = i + 1;
};

int main() {
  std::cout << Foo::i << std::endl;
}

Clang 3.7 版对此进行编译并输出 1

Live Demo

虽然 GCC 版本 5.3 发出错误:

错误:“i”未在此范围内声明

Live Demo

问:

两种编译器中哪一种符合 C++ 标准?

【问题讨论】:

  • static int const i = i + 1; 意义不大,如果要初始化为1,就初始化为1即可。
  • @Shark 确实如此。然而,这是一个语言律师问题。
  • 通常是对象is fully defined when you reach the =。不知道 const 静态成员有没有什么特殊规定。但是当然是UB读取初始化器中的value了。
  • @BoPersson 如果可能,读取不会未定义。由于不执行常量初始化(初始化器不是常量表达式),因此进行零初始化,这发生在动态初始化之前。

标签: c++ gcc clang language-lawyer static-members


【解决方案1】:

GCC 抱怨名称未声明当然是错误的,因为 i 的声明点是 immediately after its declarator

然而,GCC 在拒绝 sn-p 方面可以说是正确的。 [class.static.data]/3:

如果非易失性const 静态数据成员是整数或 枚举类型,其在类定义中的声明可以指定 一个 brace-or-equal-initializer 其中每个 initializer-clause 这是一个赋值表达式是一个常量表达式(5.20)。

为了使[expr.const]/(2.7) 不失败,它的四个子项目符号之一必须适用:

左值到右值的转换 (4.1),除非它应用于

  • 一个整数或枚举类型的非易失性左值,它指的是一个完整的非易失性const对象前面有一个 初始化,用常量表达式初始化,或
  • 非易失性泛左值,它引用字符串文字 (2.13.5) 的子对象,或
  • 一个非易失性泛左值,它引用一个用constexpr 定义的非易失性对象,或者引用这样一个非易失性子对象 对象,或
  • 文字类型的非易失性左值,它引用一个非易失性对象,其生命周期在 e 的评估内开始;

(2.7.1) 是唯一可能的候选者,但由于 i 之前没有使用初始化器初始化,因此它不适用。

注意 Clang 是 completely consistent:

constexpr int i = i;
void f() {
    // constexpr int j = j; // error
    static constexpr int h = h;
}

如果i 具有静态存储持续时间,则它似乎将i 视为在其初始化程序中“正确”初始化。我提交了错误#26858

【讨论】:

    【解决方案2】:

    在类中初始化的静态 const 成员必须由常量表达式初始化。在i 的初始化器中,i 尚未由常量表达式初始化,因此它本身也不是常量表达式。在我看来,这两个编译器都是有罪的。

    • clang,用于接受程序
    • gcc,用于提供误导性错误消息

    【讨论】:

    • 这如何回答这个问题。 -1
    • @Destructor 仅供参考,这非常直接地回答了这个问题。哪个编译器错了? ——两者,出于这样那样的原因。如果你认为这不是一个答案,你应该这样标记它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-19
    相关资源
    最近更新 更多