59

I tried to use incomplete type in nested name specifier as the following:

class A;

int b= A::c; // error: incomplete type ‘A’ used in nested name specifier

class A {
    static const int c=5;
};

There is says nothing about it in the 3.4.3/1 of N3797 working draft:

The name of a class or namespace member or enumerator can be referred to after the :: scope resolution operator (5.1) applied to a nested-name-specifier that denotes its class, namespace, or enumeration

So is that behavior implementation dependent?

Filip Roséen - refp
  • 62,493
  • 20
  • 150
  • 196
  • 1
    The part you quoted does not say that you can use a nested name after a forward declaration of a class. – R Sahu Jun 07 '14 at 08:08
  • @RSahu The name A was declared before it is used. This implies that such name using does not contradicts to the quote. Note that the name A was declared before it is used as said in the 3.4.1/4: `A name used in global scope, outside of any function, class or user-declared namespace, shall be declared before its use in global scope.` –  Jun 07 '14 at 08:10
  • should it be class A{ public const int c = 5;}; – Gavin Jun 07 '14 at 10:54
  • 2
    Formally, this code is ill-formed not because `A` is incomplete, but because the name `c` is not declared within the scope of `A` at the point where it is used, so qualified name lookup (3.4.3) fails. I imagine the compiler authors decided that "class is incomplete" (and therefore, none of its members were declared so qualified name lookup can't possibly succeed) was a more helpful error message. – Igor Tandetnik Jun 09 '14 at 19:10

2 Answers2

59

Introduction

There are several places in the standard that implicitly implies that your code is ill-formed, but the below quotation speaks for itself:

3.3.2p6 Point of declaration [basic.scope.pdecl]

After the point of declaration of a class member, the member name can be looked up in the scope of its class.

The problem with your code isn't that you try to reach inside the body of an incomplete type, the problem is that you can only refer to a class member name after it has been declared.

Since your forward-declaration (of course) doesn't introduce any member named c, it is ill-formed to refer to such name.


The misleading diagnostic...

The diagnostic issued by both gcc and clang when being fed your code is somewhat misleading, and honestly I feel a bug report is in order.

foo.cpp:3:8: error: incomplete type 'A' named in nested name specifier

We are allowed to name an incomplete type in a nested-name-specifier, but as said; we are not allowed to refer to a member that has not yet been declared.

ill-formed:

class X {
  static int a[X::x];        // ill-formed, `X::x` has not yet been declared
  static int const x = 123;
};

legal:

class X {
  int const x = 123;
  int a[X::x]; // legal, `X` is incomplete (since we are still defining it)
               //        but we can still refer to a _declared_ member of it
};
Community
  • 1
  • 1
Filip Roséen - refp
  • 62,493
  • 20
  • 150
  • 196
  • 1
    a wrong loop of includes can leed to this issue too. Check if the class header you are building with error is *wrongly* included in the class giving you the error. – fiorentinoing Jun 22 '20 at 10:00
-3

I got the same error when accessing a member variable of a class that had been forward-declared in the header, but not included in the main from where I accessed it.

myClass.h

class FowardInclude; // has member "int x", but only forward-declared here

class MyClass {
public:
    void foo(int num);
}

main.cpp

#include "forwardInclude.h" // <-- This is the line I was missing
#include "myClass.h"

MyClass m;
m.foo(ForwardInclude::x);
Zciurus
  • 786
  • 4
  • 23