9
votes

Consider the following piece of code:

#include <iostream>

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

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

Clang version 3.7 compiles this and outputs 1.

Live Demo

While GCC version 5.3 emits an error:

error: 'i' was not declared in this scope

Live Demo

Q:

Which one of the two compilers conforms to the C++ standard?

2
static int const i = i + 1; makes little sense, if you want to initialize at 1, just initialize at 1.Shark
@Shark Indeed. However this is a language lawyer question.101010
Normally the object is fully defined when you reach the =. Don't know if there are any special rules for const static members. But of course it is UB to read the value in the initializer.Bo Persson
@BoPersson The read would not be undefined if possible. Since constant initialization is not performed (the initializer is not a constant expression), zero-initialization is, which happens before dynamic initialization.Columbo

2 Answers

4
votes

GCC is of course wrong to complain about the name being undeclared, since the point of declaration of i is immediately after its declarator.

However, GCC is arguably right in rejecting the snippet overall. [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).

And for [expr.const]/(2.7) not to fail, one of its four sub bullets must apply:

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) is the only plausible candidate, but since i has not previously been initialized using an initializer, it doesn't apply.

Note that Clang is completely consistent:

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

It appears that it treats i as "properly" initialized in its initializer if it has static storage duration. I filed bug #26858.

2
votes

A static const member initialized in-class must be initialized by a constant expression. In the initializer of i, i is not initialized by a constant expression (yet) and so is not a constant expression itself. In my view both compilers are guilty.

  • clang, for accepting the program
  • gcc, for giving a misleading error message