Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When can I use a forward declaration?

I am looking for the definition of when I am allowed to do forward declaration of a class in another class's header file:

Am I allowed to do it for a base class, for a class held as a member, for a class passed to member function by reference, etc. ?

like image 382
Igor Avatar asked Feb 16 '09 15:02

Igor


People also ask

What can you do with a forward declaration?

A forward declaration tells the compiler about the existence of an entity before actually defining the entity. Forward declarations can also be used with other entity in C++, such as functions, variables and user-defined types.

Why use forward declaration 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.

Where do you put forward declaration?

Generally you would include forward declarations in a header file and then include that header file in the same way that iostream is included.

What is the advantage of forward declaration?

One big benefit is faster compilation times, as a forward declaration takes less time for the compiler to parse than a complete class definition. This can make a very significant difference for large projects with many include files.


2 Answers

Put yourself in the compiler's position: when you forward declare a type, all the compiler knows is that this type exists; it knows nothing about its size, members, or methods. This is why it's called an incomplete type. Therefore, you cannot use the type to declare a member, or a base class, since the compiler would need to know the layout of the type.

Assuming the following forward declaration.

class X; 

Here's what you can and cannot do.

What you can do with an incomplete type:

  • Declare a member to be a pointer or a reference to the incomplete type:

    class Foo {     X *p;     X &r; }; 
  • Declare functions or methods which accept/return incomplete types:

    void f1(X); X    f2(); 
  • Define functions or methods which accept/return pointers/references to the incomplete type (but without using its members):

    void f3(X*, X&) {} X&   f4()       {} X*   f5()       {} 

What you cannot do with an incomplete type:

  • Use it as a base class

    class Foo : X {} // compiler error! 
  • Use it to declare a member:

    class Foo {     X m; // compiler error! }; 
  • Define functions or methods using this type

    void f1(X x) {} // compiler error! X    f2()    {} // compiler error! 
  • Use its methods or fields, in fact trying to dereference a variable with incomplete type

    class Foo {     X *m;                 void method()                 {         m->someMethod();      // compiler error!         int i = m->someField; // compiler error!     } }; 

When it comes to templates, there is no absolute rule: whether you can use an incomplete type as a template parameter is dependent on the way the type is used in the template.

For instance, std::vector<T> requires its parameter to be a complete type, while boost::container::vector<T> does not. Sometimes, a complete type is required only if you use certain member functions; this is the case for std::unique_ptr<T>, for example.

A well-documented template should indicate in its documentation all the requirements of its parameters, including whether they need to be complete types or not.

like image 171
Luc Touraille Avatar answered Oct 30 '22 23:10

Luc Touraille


The main rule is that you can only forward-declare classes whose memory layout (and thus member functions and data members) do not need to be known in the file you forward-declare it.

This would rule out base classes and anything but classes used via references and pointers.

like image 37
Timo Geusch Avatar answered Oct 31 '22 00:10

Timo Geusch