HOWTO · C++

#pragma once en C++: Guardas de inclusión y portabilidad

Evite inclusiones repetidas de encabezados en C++ con #pragma once o guardas de inclusión y elija según su compilador.

En esta página

Use #pragma once al inicio de un encabezado C o C++ normal si todos los compiladores compatibles con el proyecto lo implementan. Hace que el archivo se procese como máximo una vez por unidad de traducción. Use una guarda de inclusión #ifndef/#define con un nombre único si necesita portabilidad estándar, admite un compilador desconocido o sigue una convención existente. Ninguna opción limita el encabezado a un solo uso en todo el programa.

Colocar #pragma once antes de las declaraciones

// config.hpp
#pragma once

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

La directiva no lleva punto y coma. El preprocesador maneja #include antes de compilar, así que #pragma once evita procesar el texto otra vez al construir una unidad de traducción. Es una extensión muy implementada, incluida en GCC, Clang y MSVC, pero no es una directiva ISO C ni ISO C++. GCC la documenta como alternativa a los encabezados de inclusión única; compruebe las toolchains que el proyecto realmente promete admitir.

Verificar la inclusión directa e indirecta

Este caso exacto de tres archivos incluye config.hpp mediante server.hpp y de forma directa desde 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;
}

Guarde los tres archivos en el mismo directorio y ejecute:

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

Con #pragma once en config.hpp, no hay salida y ./app termina con estado 0. Se verificó con g++ (Ubuntu 15.2.0-16ubuntu1) 15.2.0. Quite solo la directiva de config.hpp: GCC termina con estado 1 y diagnostica la redefinición de Config, indicando la inclusión directa y la anterior indirecta. Es una regla por unidad de traducción; el ejemplo no se probó por separado en MSVC ni Clang.

Usar una guarda de inclusión estándar

// config.hpp
#ifndef EXAMPLE_CONFIG_HPP
#define EXAMPLE_CONFIG_HPP

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

#endif  // EXAMPLE_CONFIG_HPP

La primera inclusión define el macro y las posteriores omiten el texto. Elija un nombre descriptivo y único, formado por proyecto, directorio y archivo. CONFIG_H puede colisionar con otro encabezado; los identificadores con __ o que empiezan por _ seguido de mayúscula están reservados a la implementación.

#include es inclusión textual: antes de comprobar C++, el preprocesador sustituye la directiva por el texto del encabezado. En el ejemplo, config.hpp llega primero mediante server.hpp y después directamente a main.cpp; sin protección el compilador recibe dos definiciones de Config. Por eso la protección pertenece al encabezado y no solo a un archivo fuente que lo incluye. Es un error de compilación, no un error del enlazador de archivos compilados por separado.

Elegir el mecanismo

Situación Opción Motivo
Todos los compiladores de destino admiten #pragma once #pragma once Es breve y no colisiona con macros.
Biblioteca pública, compilador desconocido o portabilidad estricta Guarda de inclusión Son directivas estándar.
Convención existente La convención Mantiene los encabezados coherentes.
Encabezado incluido deliberadamente varias veces, como una lista X-macro Ninguno de forma predeterminada Cada inclusión es intencional.

No añada ambos mecanismos a todos los encabezados: uno suele bastar y los compiladores reconocen las guardas convencionales. Tampoco afirme una mejora universal de velocidad; mídala. #pragma once depende de que la implementación reconozca la identidad del archivo, por lo que alias, archivos generados, sistemas de archivos en red o rutas inusuales pueden importar. Las guardas evitan esa cuestión, pero requieren un macro único.

Lo que no resuelve la protección

La protección actúa por unidad de traducción y no corrige todos los errores de enlazador por definiciones múltiples ni la One Definition Rule (ODR). Una función libre no inline definida en un encabezado puede crear una definición externa en cada .cpp; coloque las definiciones normales en un .cpp, o use deliberadamente inline o plantillas. Tampoco resuelve un ciclo en el que ambos tipos necesitan la definición completa: use una declaración adelantada para punteros o referencias, o mueva la implementación. Los módulos C++20 son distintos: import no incluye texto de encabezado.

Resumen

Use #pragma once cuando la extensión sea aceptable y guardas de inclusión únicas para portabilidad estándar. Compruebe la ruta directa e indirecta y diagnostique aparte ODR, ciclos, encabezados de inclusión repetida y módulos.