Skip to content

Forward Declarations

Goal: Minimize compile-time dependencies

When to Forward Declare

✅ DO Forward Declare When:

1. Pointer or Reference Member

// header.hpp
class EventDispatcher;  // Forward declaration

class MyClass {
    EventDispatcher* dispatcher_;  // Pointer - OK
};

2. Pointer or Reference Parameter

class Parameter;  // Forward declaration

void process_parameter(Parameter& param);  // Reference - OK
void set_parameter(Parameter* param);      // Pointer - OK

3. Return Type (Pointer/Reference)

class Buffer;  // Forward declaration

Buffer* get_buffer();        // Pointer return - OK
Buffer& get_buffer_ref();    // Reference return - OK

❌ DON'T Forward Declare When:

1. Value Member

#include "parameter.hpp"  // MUST include

class MyClass {
    Parameter param_;  // Value member - needs complete type
};

2. Inheritance

#include "base_class.hpp"  // MUST include

class Derived : public BaseClass {  // Inheritance - needs complete type
};

3. Template Parameter

#include "processor.hpp"  // MUST include

std::vector<Processor> processors_;  // Template - needs complete type

4. Using Member Functions/Data

#include "buffer.hpp"  // MUST include

void foo(Buffer& buf) {
    buf.resize(100);  // Using methods - needs complete type
}

Proper Forward Declaration Syntax

Classes

namespace audiolab {
    class AudioProcessor;
    struct BufferConfig;
}

Templates

namespace audiolab {
    template<typename T> class RingBuffer;
}

Nested Classes

class Outer {
    class Inner;  // Forward declare nested class
};

Example: Minimize Dependencies

Bad (Includes Everything)

// my_processor.hpp
#include "event_dispatcher.hpp"  // Full include
#include "parameter.hpp"         // Full include
#include "buffer.hpp"            // Full include

class MyProcessor {
    EventDispatcher* dispatcher_;  // Could use forward declaration
    Parameter* param_;             // Could use forward declaration
    Buffer buffer_;                // Needs full include (value member)
};

Good (Minimal Dependencies)

// my_processor.hpp
#include "buffer.hpp"  // Only what's needed

namespace audiolab {
    class EventDispatcher;  // Forward declaration
    class Parameter;        // Forward declaration
}

class MyProcessor {
    EventDispatcher* dispatcher_;  // Forward declared
    Parameter* param_;             // Forward declared
    Buffer buffer_;                // Fully included
};
// my_processor.cpp
#include "my_processor.hpp"
#include "event_dispatcher.hpp"  // Now include for implementation
#include "parameter.hpp"         // Now include for implementation

// Can use full API here

Benefits

  1. Faster Compilation: Less to parse in header
  2. Reduced Coupling: Changes to forward-declared class don't trigger recompile
  3. Circular Dependencies: Can break circular dependencies

Limitations

Can't: - Create instances (value members) - Call methods - Access data members - Use sizeof() - Inherit from

Can: - Declare pointers/references - Declare function parameters/returns - Use in typedef/using


Rule of Thumb: If you only mention the type's name (not use it), forward declare.