HOWTO · C++

#pragma once in C++: Include Guards and Portability

Prevent repeated C++ header inclusion with #pragma once or include guards, and choose the right option for your toolchain.

Use #pragma once at the start of an ordinary C or C++ header when every compiler your project supports implements it. It tells that implementation to include the file at most once in each translation unit. Use a uniquely named #ifndef/#define include guard when you need standard preprocessor portability, support an unknown compiler, or follow an existing project convention. Neither option makes a header occur only once in the whole program.

Put #pragma once Before Header Declarations

Place the directive before the declarations that need protection. It has no semicolon:

// config.hpp
#pragma once

class Config {
public:
    int port() const { return 8080; }
};

The preprocessor handles #include before compilation. #pragma once therefore prevents the protected header text from being processed again while the preprocessor builds one translation unit. It is widely implemented, including by GCC, Clang, and MSVC, but it is not an ISO C or ISO C++ standard directive. GCC documents it as an alternative to once-only header guards, and Microsoft documents the MSVC form. Check the compilers your project actually promises to support rather than assuming universal support.

Verify Direct and Indirect Inclusion

The same header can arrive through two paths. This exact three-file fixture includes config.hpp through server.hpp and directly from main.cpp:

// server.hpp
#pragma once
#include "config.hpp"

class Server { Config config_; };
// main.cpp
#include "server.hpp"
#include "config.hpp"

int main() {
    return Config{}.port() == 8080 ? 0 : 1;
}

Save config.hpp, server.hpp, and main.cpp in one directory, then compile and run:

g++ -std=c++17 -Wall -Wextra main.cpp -o app
./app

With #pragma once in config.hpp, this command produces no terminal output and ./app exits with status 0. The fixture was run with g++ (Ubuntu 15.2.0-16ubuntu1) 15.2.0.

To see the problem it prevents, remove only #pragma once from config.hpp and run the compile command again. GCC exits with status 1 and reports error: redefinition of ‘class Config’; it identifies the direct inclusion at main.cpp:2 and the earlier inclusion through server.hpp. The diagnostic is expected: the class definition is now pasted into the same translation unit twice. The example demonstrates preprocessor inclusion, not behavior separately tested on MSVC or Clang.

Use a Standard Include Guard Instead

An include guard uses only standard preprocessor directives. On the first inclusion, it defines a macro; later inclusions see the macro and skip the enclosed text:

// config.hpp
#ifndef EXAMPLE_CONFIG_HPP
#define EXAMPLE_CONFIG_HPP

class Config {
public:
    int port() const { return 8080; }
};

#endif  // EXAMPLE_CONFIG_HPP

Make the macro descriptive and unique across the project, commonly from a project prefix plus its directory and file name. EXAMPLE_CONFIG_HPP is safer than a generic CONFIG_H: two unrelated headers using the same guard can cause one header’s contents to be silently skipped. Also avoid identifiers containing __, or beginning with _ followed by an uppercase letter; those forms are reserved to the implementation.

Why a Repeated Include Is a Problem

#include is textual inclusion: before the compiler checks C++ declarations, the preprocessor replaces each include directive with the selected header’s contents. In the fixture, main.cpp first receives server.hpp, which itself receives config.hpp; the next line in main.cpp requests config.hpp again. Without a once-only mechanism, the compiler receives two definitions of Config in that one translation unit.

This is also why the protection belongs in the header being protected, rather than only in a source file that happens to include it. A later source file, test, or another header can produce a different include path. Keep declarations intended for shared use in the protected header. Do not confuse this compile-time repeated-definition error with a linker diagnostic produced after separate source files have been compiled.

Choose the Header Protection Mechanism

Use this decision guide for a normal header:

Situation Appropriate choice Reason
All supported compilers implement #pragma once #pragma once It is concise and has no guard-macro collision.
Public library, unknown compiler, or strict standard portability Include guard #ifndef and #define are standard preprocessor directives.
Existing repository convention The established convention Consistency makes headers easier to maintain.
Header deliberately included multiple times, such as an X-macro list Neither by default Repeated inclusion is its intended behavior; protect only the parts that must be once-only.

Do not put both mechanisms in every ordinary header by default. They normally add no useful protection beyond either one alone, and compilers can recognize conventional include guards as once-only headers. Likewise, do not claim one is always faster: measure the project if build time is the reason to change a convention.

#pragma once asks the implementation to determine whether two include paths refer to the same file. This is usually straightforward, but aliases, generated files, network filesystems, or unusual include paths can make file identity relevant. Include guards avoid that particular question, but their macro names must remain unique. These are decision boundaries, not a reason to assume either mechanism fails in an ordinary layout.

Know What Header Protection Does Not Solve

Both mechanisms apply separately to each translation unit. If main.cpp and server.cpp include config.hpp, each translation unit legitimately processes its declarations once. Header protection does not fix every multiple-definition linker error or override the One Definition Rule (ODR).

For example, a non-inline free-function definition in a header can create an external definition in every .cpp file that includes it. Put ordinary definitions in a .cpp file, or deliberately use inline or a template when the header design requires it. Similarly, a guard can stop endless textual inclusion in a cycle, but it cannot make two types complete when each header needs the other’s full definition. Use a forward declaration when a pointer or reference is enough, or move implementation details out of a header.

C++20 modules are a different mechanism: import does not textually include a header, so an include guard is not a module solution. Moving to modules is a separate interface and build-system decision.

Summary

For a normal header, use #pragma once when its nonstandard support is acceptable, or a unique include guard when standard portability or project policy requires it. Put the protection before declarations, verify the direct-and-indirect include path, and investigate ODR, cyclic-dependency, deliberately repeated-header, and module issues separately.