9

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?

4
  • static int const i = i + 1; makes little sense, if you want to initialize at 1, just initialize at 1. Commented Mar 4, 2016 at 9:59
  • 1
    @Shark Indeed. However this is a language lawyer question. Commented Mar 4, 2016 at 10:01
  • 1
    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. Commented Mar 4, 2016 at 10:07
  • @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. Commented Mar 7, 2016 at 0:45

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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

2 Comments

How does this answer the question. -1
@Destructor Just FYI, this answers the question very directly. Which of the compilers is wrong? — Both, for such and such reasons. If you think this is not an answer, you should flag it as such.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.