Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

namespaces

People also ask

Can you forward-declare a namespace?

There is no forward declaration of namespace. You can forward declare a function.

Can you forward-declare a class?

In C++, classes and structs can be forward-declared like this: class MyClass; struct MyStruct; In C++, classes can be forward-declared if you only need to use the pointer-to-that-class type (since all object pointers are the same size, and this is what the compiler cares about).

How do you fix a forward declaration in C++?

To solve this, you can forward-declare the parts you need in one of the files and leave the #include out of that file. Hmm... the declaration of Car is required here as Wheel has a pointer to a Car , but Car. h can't be included here as it would result in a compiler error.

Why forward-declare instead of include?

A forward declaration is much faster to parse than a whole header file that itself may include even more header files. Also, if you change something in the header file for class B, everything including that header will have to be recompiled.


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;
};

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.


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 {
    }
  }
}

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;
};

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.