¿#pragma once versus incluir guardias? [duplicar]
Estoy trabajando en una base de código que se sabe que solo se ejecuta en Windows y se compila en Visual Studio (se integra estrechamente con Excel, por lo que no irá a ninguna parte). Me pregunto si debería optar por las protecciones de inclusión tradicionales o utilizarlas #pragma once
para nuestro código. Creo que dejar que el compilador se encargue #pragma once
producirá compilaciones más rápidas y será menos propenso a errores al copiar y pegar. También es un poco menos feo.;)
Nota: para obtener tiempos de compilación más rápidos, podríamos usar Redundant Include Guards , pero eso agrega un estrecho acoplamiento entre el archivo incluido y el archivo incluido. Por lo general, está bien porque la protección debe basarse en el nombre del archivo y solo cambiaría si fuera necesario cambiar el nombre de inclusión de todos modos.
No creo que haga una diferencia significativa en el tiempo de compilación, pero #pragma once
es muy compatible con todos los compiladores, pero en realidad no forma parte del estándar. El preprocesador puede ser un poco más rápido, ya que es más sencillo comprender su intención exacta.
#pragma once
es menos propenso a cometer errores y requiere menos código para escribir.
Para acelerar más el tiempo de compilación, simplemente declare hacia adelante en lugar de incluirlo en archivos .h cuando pueda.
Prefiero usar #pragma once
.
Consulte este artículo de Wikipedia sobre la posibilidad de utilizar ambos .
Solo quería agregar a esta discusión que estoy compilando en VS y GCC, y solía usar include guards. Ahora me cambié a #pragma once
, y la única razón para mí no es el rendimiento, la portabilidad o el estándar, ya que realmente no me importa qué es estándar siempre que VS y GCC lo admitan, y eso es lo siguiente:
#pragma once
reduce las posibilidades de errores.
Es muy fácil copiar y pegar un archivo de encabezado en otro archivo de encabezado, modificarlo para adaptarlo a sus necesidades y olvidarse de cambiar el nombre del protector de inclusión. Una vez incluidos ambos, le llevará un tiempo localizar el error, ya que los mensajes de error no son necesariamente claros.
#pragma once
tiene errores que no se pueden corregir . Nunca debe usarse.
Si su #include
ruta de búsqueda es lo suficientemente complicada, es posible que el compilador no pueda distinguir entre dos encabezados con el mismo nombre base (por ejemplo, a/foo.h
y b/foo.h
), por lo que #pragma once
en uno de ellos suprimirá ambos . También es posible que no pueda distinguir dos inclusiones relativas diferentes (por ejemplo, #include "foo.h"
y que #include "../a/foo.h"
hagan referencia al mismo archivo), por lo que #pragma once
no podrá suprimir una inclusión redundante cuando debería haberlo hecho.
Esto también afecta la capacidad del compilador para evitar releer archivos con #ifndef
guardias, pero eso es solo una optimización. Con #ifndef
las protecciones, el compilador puede leer de forma segura cualquier archivo que no esté seguro de haber visto ya; si está mal, sólo tiene que hacer un poco de trabajo extra. Siempre que no haya dos encabezados que definan la misma macro de protección, el código se compilará como se esperaba. Y si dos encabezados definen la misma macro de protección, el programador puede entrar y cambiar uno de ellos.
#pragma once
no tiene tal red de seguridad: si el compilador se equivoca acerca de la identidad de un archivo de encabezado, de cualquier manera , el programa no podrá compilar. Si encuentra este error, sus únicas opciones son dejar de usar #pragma once
o cambiar el nombre de uno de los encabezados. Los nombres de los encabezados son parte de su contrato API, por lo que cambiar el nombre probablemente no sea una opción.
(La versión corta de por qué esto no se puede solucionar es que ni la API del sistema de archivos de Unix ni de Windows ofrecen ningún mecanismo que garantice indicarle si dos nombres de ruta absolutos se refieren al mismo archivo. Si tiene la impresión de que los números de inodo se pueden usar para eso, lo siento, estás equivocado.)
(Nota histórica: la única razón por la que no eliminé #pragma once
GCC #import
cuando tenía la autoridad para hacerlo, hace aproximadamente 12 años, fue que los encabezados del sistema de Apple dependían de ellos. En retrospectiva, eso no debería haberme detenido).
(Dado que esto ha aparecido dos veces en el hilo de comentarios: los desarrolladores de GCC se esforzaron bastante en hacerlo #pragma once
lo más confiable posible; consulte el informe de error 11569 de GCC . Sin embargo, la implementación en las versiones actuales de GCC aún puede fallar en condiciones plausibles condiciones, como granjas de construcción que sufren desfase de reloj. No sé cómo es la implementación de ningún otro compilador, pero no esperaría que nadie lo hubiera hecho mejor ).
Hasta que el día #pragma once
se convierta en estándar (eso no es actualmente una prioridad para los estándares futuros), le sugiero que lo use Y use guardias, de esta manera:
#ifndef BLAH_H
#define BLAH_H
#pragma once
// ...
#endif
Las razones son:
#pragma once
no es estándar, por lo que es posible que algún compilador no proporcione la funcionalidad. Dicho esto, todos los compiladores principales lo admiten. Si un compilador no lo sabe, al menos lo ignorará.- Como no existe un comportamiento estándar para
#pragma once
, no debes asumir que el comportamiento será el mismo en todos los compiladores. Los guardias se asegurarán de que al menos la suposición básica sea la misma para todos los compiladores que al menos implementen las instrucciones de preprocesador necesarias para los guardias. - En la mayoría de los compiladores,
#pragma once
acelerará la compilación (de un cpp) porque el compilador no volverá a abrir el archivo que contiene esta instrucción. Por lo tanto, tenerlo en un archivo podría ayudar o no, según el compilador. Escuché que g++ puede hacer la misma optimización cuando se detectan guardias, pero hay que confirmarlo.
Al usar los dos juntos, obtienes lo mejor de cada compilador para esto.
Ahora, si no tiene algún script automático para generar los guardias, podría ser más conveniente simplemente usar #pragma once
. Solo sepa lo que eso significa para el código portátil. (Estoy usando VAssistX para generar guardias y pragma una vez rápidamente)
Casi siempre deberías pensar tu código de forma portátil (porque no sabes de qué está hecho el futuro), pero si realmente crees que no debe compilarse con otro compilador (código para hardware integrado muy específico, por ejemplo) entonces deberías consultar la documentación del compilador para #pragma once
saber qué estás haciendo realmente.