Является ли #pragma безопасным включением охраны? - PullRequest
265 голосов
/ 25 апреля 2009

Я читал, что при использовании #pragma once существует некоторая оптимизация компилятора, которая может привести к более быстрой компиляции. Я признаю, что это нестандартно и, следовательно, может создать проблему кросс-платформенной совместимости.

Поддерживается ли это большинством современных компиляторов на платформах, отличных от Windows (gcc)?

Я хочу избежать проблем с компиляцией платформы, но также хочу избежать дополнительной работы защитников:

#pragma once
#ifndef HEADER_H
#define HEADER_H

...

#endif // HEADER_H

Должен ли я быть обеспокоен? Должен ли я потратить на это еще какую-нибудь умственную энергию?

Ответы [ 14 ]

306 голосов
/ 22 декабря 2009

#pragma once имеет один недостаток (кроме нестандартного), и это если у вас один и тот же файл в разных местах (у нас это есть, потому что наша система сборки копирует файлы), тогда компилятор будет думать, что они разные файлы.

160 голосов
/ 25 апреля 2009

Использование #pragma once должно работать на любом современном компиляторе, но я не вижу никакой причины не использовать стандартный #ifndef include guard. Работает просто отлично. Единственное предупреждение: GCC не поддерживал #pragma once до версии 3.4 .

.

Я также обнаружил, что по крайней мере в GCC он распознает стандарт #ifndef include guard и оптимизирует его , поэтому он не должен быть намного медленнее, чем #pragma once.

56 голосов
/ 25 апреля 2009

Хотелось бы, чтобы #pragma once (или что-то подобное) было в стандарте. Включить охранников не так уж и сложно (но их, кажется, немного сложно объяснить людям, изучающим язык), но это кажется незначительным раздражением, которого можно было избежать.

Фактически, поскольку в 99,98% случаев поведение #pragma once является желаемым поведением, было бы неплохо, если бы предотвращение многократного включения заголовка автоматически обрабатывалось компилятором с #pragma или чем-то разрешить двойной в том числе.

Но у нас есть то, что у нас есть (кроме того, что у вас может не быть #pragma once).

32 голосов
/ 25 апреля 2009

Я не знаю ни о каких преимуществах производительности, но это, безусловно, работает. Я использую его во всех своих проектах C ++ (если я использую компилятор MS). Я считаю, что это более эффективно, чем использование

#ifndef HEADERNAME_H
#define HEADERNAME_H
...
#endif

Он выполняет ту же работу и не заполняет препроцессор дополнительными макросами.

GCC официально поддерживает #pragma once начиная с версии 3.4 .

21 голосов
/ 28 августа 2013

GCC поддерживает #pragma once начиная с 3.4, дополнительную информацию о поддержке компилятора см. В http://en.wikipedia.org/wiki/Pragma_once.

Большой плюс, который я вижу при использовании #pragma once, в отличие от включения защиты, состоит в том, чтобы избежать ошибок копирования / вставки.

Посмотрим правде в глаза: большинство из нас едва ли начинают новый заголовочный файл с нуля, а просто копируют существующий и модифицируют его в соответствии с нашими потребностями. Гораздо проще создать рабочий шаблон, используя #pragma once вместо включенных охранников. Чем меньше мне нужно изменить шаблон, тем меньше вероятность возникновения ошибок. Наличие одного и того же встроенного средства защиты в разных файлах приводит к странным ошибкам компилятора, и требуется некоторое время, чтобы выяснить, что пошло не так.

TL; DR: #pragma once проще в использовании.

11 голосов
/ 25 апреля 2009

Я пользуюсь им и доволен этим, так как мне приходится печатать намного меньше, чтобы создать новый заголовок. У меня это работало нормально на трех платформах: Windows, Mac и Linux.

У меня нет никакой информации о производительности, но я полагаю, что разница между #pragma и включенной защитой будет ничем по сравнению с медлительностью анализа грамматики C ++. Это настоящая проблема. Попробуйте скомпилировать такое же количество файлов и строк с помощью компилятора C #, например, чтобы увидеть разницу.

В конце концов, использование охраны или прагмы не будет иметь никакого значения.

11 голосов
/ 25 апреля 2009

Использование '#pragma once' может не иметь никакого эффекта (оно не поддерживается повсеместно - хотя оно становится все более широко поддерживаемым), поэтому вам все равно нужно использовать код условной компиляции, в этом случае зачем использовать '#pragma once '? Компилятор, вероятно, все равно оптимизирует его. Это зависит от вашей целевой платформы, хотя. Если все ваши цели поддерживают это, тогда продолжайте и используйте это - но это должно быть сознательное решение, потому что весь ад развалится, если вы только используете прагму, а затем портируете на компилятор, который ее не поддерживает.

5 голосов
/ 11 апреля 2013

Не всегда.

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52566 содержит хороший пример двух файлов, которые должны быть включены в оба, но ошибочно считаются идентичными из-за идентичных меток времени и содержимого (не идентичного имени файла).

4 голосов
/ 25 апреля 2009

Преимущество в производительности заключается в том, что нет необходимости повторно открывать файл после прочтения #pragma. С защитой, компилятор должен открыть файл (который может быть дорогостоящим по времени), чтобы получить информацию, что он не должен снова включать его содержимое.

Это теория только потому, что некоторые компиляторы автоматически не открывают файлы, в которых не было прочитанного кода, для каждой единицы компиляции.

В любом случае, это относится не ко всем компиляторам, поэтому в идеале нужно избегать использования #pragma, поскольку кросс-платформенный код вообще не является стандартным / не имеет стандартизированного определения и эффекта. Однако, на самом деле, это действительно лучше, чем охранники.

В конце, лучшее предложение , которое вы можете получить , чтобы быть уверенным в максимальной скорости вашего компилятора, без необходимости проверять поведение каждого компилятора в этом случае, состоит в том, чтобы использовать обе прагмы один раз и охранники.

#ifndef NR_TEST_H
#define NR_TEST_H
#pragma once

#include "Thing.h"

namespace MyApp
{
 // ...
}

#endif

Таким образом, вы получаете лучшее из обоих (кроссплатформенность и скорость компиляции справки).

Поскольку он дольше печатается, я лично использую инструмент, который помогает генерировать все это очень хитрым способом (Visual Assist X).

2 голосов
/ 17 декабря 2011

Основным отличием является то, что компилятору пришлось открыть файл заголовка, чтобы прочитать include guard. Для сравнения, однажды прагма заставляет компилятор отслеживать файл и не выполнять какой-либо ввод-вывод файла, когда он сталкивается с другим включением для того же файла. Хотя это может показаться незначительным, оно может легко масштабироваться с огромными проектами, особенно без хорошего заголовка, включая дисциплины.

Тем не менее, в наши дни компиляторы (включая GCC) достаточно умны, чтобы когда-то относиться к таким охранникам, как прагма. то есть они не открывают файл и избегают штрафа ввода-вывода файла.

В компиляторах, которые не поддерживают прагму, я видел ручные реализации, которые немного громоздки ..

#ifdef FOO_H
#include "foo.h"
#endif

Мне лично нравится подход #pragma, так как он позволяет избежать хлопот с именами и потенциальных опечаток. Это также более элегантный код для сравнения. Тем не менее, для переносимого кода не должно быть вреда иметь оба, если компилятор не жалуется на это.

...