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.
static int const i = i + 1;makes little sense, if you want to initialize at 1, just initialize at 1.=. 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.