C++ Export Keyword: From Templates to Modules
This article explores the evolution of the export
keyword in C++, detailing its origins as an impractical template
mechanism in C++98, its subsequent abandonment in C++11, and its modern
revival as the foundational syntax for C++20 modules.
The Original Purpose: Exported Templates in C++98
When the original C++98 standard was ratified, template
implementations typically had to reside entirely within header files.
Because the compiler needs to see the definition of a template to
instantiate it with a specific type, defining templates in source files
(.cpp) led to linker errors.
To solve this, the standards committee introduced
export template. The goal was to allow developers to
declare a template in a header file and define it in a separate
translation unit, just like standard non-template functions. In theory,
this promised cleaner code separation, hidden implementation details,
and potentially faster compilation times.
Why the Original Feature Failed
In practice, export template was an engineering disaster
for compiler implementers.
Unlike normal functions, templates cannot be compiled directly to
machine code without their concrete types. Implementing
export meant that the compiler and linker had to store,
serialize, and reconstruct abstract syntax trees (ASTs) or complex
intermediate representations across translation units.
The feature was so difficult to implement that virtually no major compiler vendor supported it. GCC, Clang (which did not yet exist, but later declined to implement it), and Microsoft Visual C++ never supported exported templates. The only widely known implementation came from the Edison Design Group (EDG) front-end, famously used by the Comeau C++ compiler. Even then, the build-time improvements were negligible, and in some cases, build performance actually degraded.
Deprecation and Removal in C++11
Because almost no one could use the feature and vendors found the maintenance burden untenable, the ISO C++ Committee formally voted to eliminate exported templates in C++11.
However, the committee did not release export back into
the wild as an ordinary identifier. Instead, the functionality was
removed, but the token export was retained as an unused,
reserved keyword. Keeping export reserved ensured that
developers could not use it as a variable or function name, preventing
future backward-compatibility breaks if the committee ever decided to
repurpose it.
The Revival: C++20 Modules
The reserved keyword remained dormant until the development of C++20,
which introduced a native module system designed to replace the legacy,
preprocessor-based #include model.
Rather than inventing a new keyword, the committee repurposed
export to define the public interface of a module. In
modern C++, export is used in two primary ways:
- Exporting Module Declarations: Declaring that a
translation unit is a module interface unit that can be imported by
other files (e.g.,
export module Math;). - Exporting Entities: Marking specific functions,
classes, templates, namespaces, or concepts as visible to external
consumers who import the module (e.g.,
export int add(int a, int b);).
Any declaration within a module that is not prefixed with
export remains internal to that module, providing the
strong encapsulation and compile-time isolation that the original C++98
designers initially envisioned, but through a fundamentally different
and far more scalable architectural model.