Skip to content

CONCEPT Cited by 1 source

Forward declaration (C++)

Definition

A forward declaration in C++ names an identifier (class / struct / function / enum / alias) without giving its full definition:

namespace Figma {
    struct Animal;
    struct Dog;
    struct Cat;
    enum struct AnimalType;
    using Feline = Cat;
};

The compiler accepts this as a promise that the definition exists somewhere; it can be used for any reference that doesn't need to know the type's size or inheritance structure — pointers, references, return types, and parameter types of function signatures. Usages that do need the full definition (sizeof, member access, inheritance, value instantiation) still require the real header.

Why it matters to build times

In the concepts/c-plus-plus-compilation-model, every #include "foo.h" drags the bytes of foo.h (and its entire transitive closure) into the current translation unit. If foo.h is only needed for a pointer-to-Foo field in a class declaration, forward-declaring struct Foo; in the current header instead of #include "foo.h" cuts the entire transitive tree out of the pre-processed mega-file.

This is the lever for the "used-but-enormous include" class of build-time regression that unused-include tooling cannot catch by construction: the include is genuinely used, just too heavy.

When it applies

Usage Needs full definition? Forward-decl works?
Foo*, Foo& No Yes
Foo(Foo& other) signature No Yes
unique_ptr<Foo> field No (incomplete-type OK) Yes (mostly)
Foo f; value member Yes (sizeof) No
class Bar : public Foo Yes No
f.method() Yes No
sizeof(Foo) Yes No

Where to put forward declarations

Naive: each file that needs a forward declaration writes its own, inline. Downsides:

  • Searchability suffers. grep "struct Foo" now returns the real declaration and every file that forward-declared it.
  • Readability suffers. Top-of-file forward-declaration blocks accumulate and clutter every header.
  • Maintenance burden. Renames have to update every forward-declaration site, not just the definition.

Figma's answer — patterns/centralized-forward-declarations — is to create one Fwd.h per directory containing every forward declaration other files in that directory need, and include it from every header in the directory. Source files never include Fwd.h (forward declarations only help in headers where they break include chains).

Source-file-only rule

Forward declarations in .cpp files don't save anything: source files don't participate in other TUs' include graphs, so they can't propagate transitive-tree shrinkage. The rule "source files never include Fwd.h" is a consequence of this: Fwd.h's purpose is to prevent header files from pulling in heavy includes, and source files are leaves.

Seen in

Last updated · 200 distilled / 1,178 read