Why Is Pragma Once Not Standardized in C++?
Despite being supported by nearly every major compiler—including GCC,
Clang, and MSVC—#pragma once has never been adopted into
the official ISO C++ standard. This directive acts as an alternative to
traditional header guards, instructing the preprocessor to include a
file only once during compilation. While developers favor it for its
brevity and immunity to copy-paste naming collisions, the ISO C++
committee has consistently avoided standardizing it due to the
near-impossible task of creating an unambiguous, portable definition of
file identity, the presence of optimized include guards, and the
emergence of C++20 modules.
The primary technical roadblock to standardizing
#pragma once is determining what constitutes "the same
file." The C++ Standard operates on an abstract machine that
historically avoided dependencies on specific operating systems or
filesystem implementations. In modern development environments,
identifying whether two paths refer to the exact same file is fraught
with edge cases:
- Hard Links and Symbolic Links: Two completely different paths can point to the same physical file on disk. A compiler tracking included files by absolute path string will treat a symlink as a distinct file, accidentally including it multiple times.
- Network Filesystems (NFS/SMB): Network mounts and distributed caching systems often lack persistent, unique file identifiers (like inode numbers) or may report inconsistent metadata across different compilation nodes.
- Build Systems and Containers: Build systems often map the same source file into multiple directories, mount identical files in temporary scratchspaces, or generate identical files in distinct build trees.
- Case-Insensitive Systems: Operating systems like Windows and macOS handle casing differently than Linux. Resolving canonical names reliably across all host and target platform combinations introduces significant overhead and platform-specific edge cases.
To standardize #pragma once, the ISO committee would
have to mandate precise semantics for file equivalence that work
uniformly across every platform that supports C++, from standard desktop
OSs to obscure bare-metal embedded environments without a traditional
filesystem.
Another major reason for the lack of standardization is that the problem had already been solved deterministically by standard header guards:
#ifndef MY_HEADER_H
#define MY_HEADER_H
// Declarations
#endifWhile verbose and susceptible to naming conflicts, macro guards
operate entirely within the lexical and preprocessor scope defined by
the standard. They do not depend on the underlying host filesystem.
Furthermore, all major modern compilers implement an optimization known
as the "multiple-include optimization." Once the preprocessor detects
that an entire file is wrapped in an #ifndef guard, it
records the controlling macro and skips opening or parsing the file on
subsequent includes if that macro is defined. Consequently, the
performance difference between #pragma once and standard
header guards in production compilers is negligible.
Finally, the focus of the C++ standard committee moved away from
patching the legacy preprocessor mechanism toward structural language
improvements. With the introduction of C++20 Modules, the entire
paradigm of textual header inclusion was superseded. Modules eliminate
the need for header guards and #pragma once entirely by
treating imported translation units as distinct, pre-compiled semantic
interfaces rather than raw text injected via #include.
Standardizing #pragma once today would expend committee
resources on standardizing a flawed, legacy preprocessor feature that
the language is actively moving away from.