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.
Related¶
- concepts/c-plus-plus-compilation-model — why forward-declaration saves bytes.
- patterns/centralized-forward-declarations — the
Fwd.h-per-directory formalization. - systems/includes-py — the CI tool that flags the regressions that motivate forward-declaring.
- systems/diwydu — complementary tool; catches includes that aren't used at all.
Seen in¶
- sources/2024-04-27-figma-speeding-up-c-build-times — Figma's
Fwd.h-per-directory rollout; cited as the recommended fix whenincludes.pyflags a byte regression and the include in question is genuinely used.