Why can't I forward-declare a class in a namespace using double colons?
Asked Answered
P

5

173
class Namespace::Class;

Why do I have to do this?:

namespace Namespace {
    class Class;
}

Using VC++ 8.0, the compiler issues:

error C2653: 'Namespace' : is not a class or namespace name

I assume that the problem here is that the compiler cannot tell whether Namespace is a class or a namespace? But why does this matter since it's just a forward declaration?

Is there another way to forward-declare a class defined in some namespace? The syntax above feels like I'm "reopening" the namespace and extending its definition. What if Class were not actually defined in Namespace? Would this result in an error at some point?

Propraetor answered 13/1, 2010 at 19:41 Comment(4)
Let me disagree with all answers here, and say it's merely a design bug of the language. They could think better.Banter
This is tending towards a discussion of why this is illegal in C++ (which is subjective), and it's looking argumentative. Voting to close.Surah
How is the compiler supposed to know that in A::B the A is a namespace identifier instead of a class name?Pontius
@STingRaySC: The discussion is subjective in that there isn't a clear answer why C++ does this, so we're speculating. (The question is a shotgun question, with some questions with objective answers, which have been answered already.) At that point, I become sensitive to traces of argument, and your agreement with Pavel that this is a misfeature of C++ qualifies. I'd have no problem with a question of why it matters if Namespace is a class or namespace. Just don't get anywhere near the hint of a possibility of conceivably starting a language flame war over syntax.Surah
Z
92

Because you can't. In C++ language fully-qualified names are only used to refer to existing (i.e. previously declared) entities. They can't be used to introduce new entities.

And you are in fact "reopening" the namespace to declare new entities. If the class Class is later defined as a member of different namespace - it is a completely different class that has nothing to do with the one you declared here.

Once you get to the point of defining the pre-declared class, you don't need to "reopen" the namespace again. You can define it in the global namespace (or any namespace enclosing your Namespace) as

class Namespace::Class {
  /* whatever */
};

Since you are referring to an entity that has already been declared in namespace Namespace, you can use qualified name Namespace::Class.

Zubkoff answered 13/1, 2010 at 19:46 Comment(3)
@STingRaySC: The only way to forward-declare a nested class is to put the declaration inside the definition of the enclosing class. And there's indeed no way to forward-declare the nested class before the definition of the enclosing class.Zubkoff
@STingRaySC: A nested class can be fwd declared -- see my answer.Diurnal
@John Dibling: Nested class is a class declared inside another class. A class declared immediately inside a namespace is not a nested class. There's nothing about sensted classes in your answer.Zubkoff
D
204

You're getting correct answers, let me just try re-wording:

class Namespace::Class;

Why do I have to do this?

You have to do this because the term Namespace::Class is telling the compiler:

...OK, compiler. Go find the namespace named Namespace, and within that refer to the class named Class.

But the compiler doesn't know what you're talking about because it doesn't know any namespace named Namespace. Even if there were a namespace named Namespace, as in:

namespace Namespace
{
};

class Namespace::Class;

it still wouldn't work, because you can't declare a class within a namespace from outside that namespace. You have to be in the namespace.

So, you can in fact forward declare a class within a namespace. Just do this:

namespace Namespace
{
    class Class;
};
Diurnal answered 13/1, 2010 at 22:14 Comment(1)
All other answers were confusing to me but this "you can't declare a class within a namespace from outside that namespace. You have to be in the namespace." was very helpful hint to remember.Pin
Z
92

Because you can't. In C++ language fully-qualified names are only used to refer to existing (i.e. previously declared) entities. They can't be used to introduce new entities.

And you are in fact "reopening" the namespace to declare new entities. If the class Class is later defined as a member of different namespace - it is a completely different class that has nothing to do with the one you declared here.

Once you get to the point of defining the pre-declared class, you don't need to "reopen" the namespace again. You can define it in the global namespace (or any namespace enclosing your Namespace) as

class Namespace::Class {
  /* whatever */
};

Since you are referring to an entity that has already been declared in namespace Namespace, you can use qualified name Namespace::Class.

Zubkoff answered 13/1, 2010 at 19:46 Comment(3)
@STingRaySC: The only way to forward-declare a nested class is to put the declaration inside the definition of the enclosing class. And there's indeed no way to forward-declare the nested class before the definition of the enclosing class.Zubkoff
@STingRaySC: A nested class can be fwd declared -- see my answer.Diurnal
@John Dibling: Nested class is a class declared inside another class. A class declared immediately inside a namespace is not a nested class. There's nothing about sensted classes in your answer.Zubkoff
F
19

I suppose it's for the same reason you cannot declare nested namespaces in one go like this:

namespace Company::Communications::Sockets {
}

and you have to do this:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}
Fayre answered 13/1, 2010 at 22:18 Comment(4)
This is not actually an answer explaining why you cannot do it.Wynellwynn
This is an answer which saved great amount of my timeCassiani
C++17 adds this.Glutamate
Here you know that all are namespaces. But with class Company::Communications::Socket you don't know if Communications is a namespace or a class (where socket is the nested class).Terrie
A
13

It would not be clear what a forward declared variable's type actually is. The forward declaration class Namespace::Class; could mean

namespace Namespace {
  class Class;
}

or

class Namespace {
public:
  class Class;
};
Aviary answered 27/7, 2018 at 6:52 Comment(2)
Isn't it impossible to forward-declare a class that's embedded into a class?Mariettemarigold
I think this is one of the best answers, because it answers why this can not easily determined by the compiler itself.Verbatim
M
3

There's a lot of excellent answers about the rationale involved in disallowing it. I just want to provide the boring standardese clause the specifically prohibits it. This holds true for C++17 (n4659).

The paragraph in question is [class.name]/2:

A declaration consisting solely of class-key identifier; is either a redeclaration of the name in the current scope or a forward declaration of the identifier as a class name. It introduces the class name into the current scope.

The above defines what constitutes a forward declaration (or redclaration of a class). Essentially, it must be one of class identifier;, struct identifier; or union identifier; where identifer is the common lexical definition in [lex.name]:

identifier:
  identifier-nondigit
  identifier identifier-nondigit
  identifier digit
identifier-nondigit:
  nondigit
  universal-character-name
nondigit: one of
  a b c d e f g h i j k l m
  n o p q r s t u v w x y z
  A B C D E F G H I J K L M
  N O P Q R S T U V W X Y Z _
digit: one of
  0 1 2 3 4 5 6 7 8 9

Which is the production of the common scheme [a-zA-Z_][a-zA-Z0-9_]* we are all familiar with. As you can see, this precludes class foo::bar; from being a valid forward declaration, because foo::bar is not an identifier. It's a fully qualified name, something different.

Motley answered 12/12, 2017 at 17:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.