class 嵌套静态常量成员变量初始化 Clang vs GCC 哪个编译器是对的?

In class nested static const member variable initialization Clang vs GCC which compiler is right?

考虑以下代码:

#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 发出错误时:

error: 'i' was not declared in this scope

Live Demo

问:

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

在-class中初始化的静态常量成员必须由常量表达式初始化。在 i 的初始化器中,i 没有被常量表达式初始化(​​还),因此它本身也不是常量表达式。在我看来,两个编译器都是有罪的。

  • clang,接受程序
  • gcc,给出误导性的错误信息

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

但是,可以说 GCC 拒绝整个片段是正确的。 [class.static.data]/3:

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment- expression is a constant expression (5.20).

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

an lvalue-to-rvalue conversion (4.1) unless it is applied to

  • a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or
  • a non-volatile glvalue that refers to a subobject of a string literal (2.13.5), or
  • a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable sub-object of such an object, or
  • a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of 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 视为 "properly" 在其初始化程序中初始化。我提交了错误 #26858.