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

#endif

While 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.