web-dev-qa-db-ja.com

#pragmaは安全なインクルードガードですか?

#pragma onceを使用すると、コンパイラの最適化が行われ、コンパイルが高速化される可能性があることを読みました。これは非標準であり、クロスプラットフォームの互換性の問題を引き起こす可能性があることを認識しています。

これは、非Windowsプラットフォーム(gcc)上のほとんどの最新コンパイラーでサポートされているものですか?

プラットフォームのコンパイルの問題を回避したいが、フォールバックガードの余分な作業も回避したい:

#pragma once
#ifndef HEADER_H
#define HEADER_H

...

#endif // HEADER_H

心配する必要がありますか?これにさらに精神的なエネルギーを費やす必要がありますか?

266
Ryan Emerle

#pragma onceを使用すると、最新のコンパイラで動作するはずですが、標準の#ifndefインクルードガードを使用しない理由は見当たりません。それはうまく機能します。 1つの注意点は、GCCは バージョン3.4 の前に#pragma onceをサポートしていなかったことです。

また、少なくともGCCでは、 標準の#ifndefインクルードガードを認識し、それを最適化する であるため、#pragma onceよりもそれほど遅くないはずです。

161
Zifre

#pragma onceには1つの欠点(非標準以外)があり、異なる場所に同じファイルがある場合(ビルドシステムがファイルをコピーするため、これがあります)、コンパイラはこれらを異なるファイルと見なします。

308
Motti

#pragma once(またはそれに似たもの)が標準に含まれていればよかったです。インクルードガードはそれほど大きな問題ではありません(ただし、言語を学習している人に説明するのは少し難しいようです)が、回避することはできますが、ちょっとした煩わしさのようです。

実際、99.98%の時間で、#pragma onceの動作は望ましい動作です。ヘッダーの複数のインクルードを防ぐことは、#pragmaまたはダブルインクルードを許可するもので、コンパイラーによって自動的に処理されるといいでしょう。

しかし、我々は持っているものを持っています(あなたが#pragma onceを持っていないかもしれないことを除いて)。

56
Michael Burr

パフォーマンス上の利点については知りませんが、確かに機能します。私はすべてのC++プロジェクトで使用しています(MSコンパイラを使用していることを保証します)。使用するよりも効果的だと思う

#ifndef HEADERNAME_H
#define HEADERNAME_H
...
#endif

同じジョブを実行し、プリプロセッサに追加のマクロを追加しません。

GCCは#pragma onceを公式にサポートしています バージョン3.4以降

32
JaredPar

GCCは3.4以降#pragma onceをサポートしています。コンパイラのサポートについては http://en.wikipedia.org/wiki/Pragma_once をご覧ください。

ガードを含めるのではなく#pragma onceを使用することで見られる大きな利点は、コピー/貼り付けエラーを回避することです。

それに直面しましょう:私たちのほとんどは、最初から新しいヘッダーファイルを開始することはほとんどなく、単に既存のヘッダーファイルをコピーして、ニーズに合わせて変更します。インクルードガードの代わりに#pragma onceを使用して作業用テンプレートを作成する方がはるかに簡単です。テンプレートを変更する必要が少ないほど、エラーが発生する可能性が低くなります。異なるファイルに同じインクルードガードを設定すると、奇妙なコンパイラエラーが発生し、何が問題なのかを理解するのに時間がかかります。

TL; DR:#pragma onceは使いやすいです。

21
uceumern

新しいヘッダーを作成するために入力する必要がはるかに少ないため、私はそれを使用し、満足しています。 Windows、Mac、Linuxの3つのプラットフォームでうまく機能しました。

パフォーマンスに関する情報はありませんが、#pragmaとincludeガードの違いは、C++文法の解析の遅さに比べると何も変わらないと思います。それが本当の問題です。たとえば、C#コンパイラで同じ数のファイルと行をコンパイルして、違いを確認してください。

最終的に、ガードまたはプラグマを使用しても、まったく問題になりません。

11
Edwin Jarvis

常にではない。

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52566 には両方を含めることを意図した2つのファイルの素敵な例がありますが、タイムスタンプが同一であり、内容(同一のファイル名ではない)。

5
Omer

パフォーマンスの利点は、#pragmaを一度読み取った後にファイルを再度開く必要がないことです。ガードを使用すると、コンパイラはファイルを開く必要があり(時間のかかる場合があります)、そのコンテンツを再び含めるべきではないという情報を取得します。

一部のコンパイラーは、各コンパイル単位に対して、読み取りコードを持たないファイルを自動的に開かないため、これは理論です。

とにかく、すべてのコンパイラに当てはまるわけではないので、クロスプラットフォームコードでは#pragmaを一度回避する必要があります。ただし、実際には、ガードよりも優れています。

最後に、 より良い提案を得ることができます この場合、各コンパイラーの動作をチェックすることなく、コンパイラーから最高の速度を確保するために、プラグマを一度とガードの両方を使用します。

#ifndef NR_TEST_H
#define NR_TEST_H
#pragma once

#include "Thing.h"

namespace MyApp
{
 // ...
}

#endif

そうすることで、両方の長所を活用できます(クロスプラットフォームで、コンパイル速度が向上します)。

入力するのが長いので、私は個人的に非常に邪悪な方法ですべてを生成するのに役立つツールを使用します(Visual Assist X)。

4
Klaim

非常に大きなツリーでgcc 3.4および4.1を使用して( distcc を使用することもあります)、#pragmaを標準インクルードガードの代わりに、または標準のインクルードガードと組み合わせて一度使用すると、速度が向上することはまだありません。

実際の節約がないので、gccの古いバージョンや他のコンパイラーを混乱させる可能性があるとは思いません。さまざまなデリンターのすべてを試したことはありませんが、それらの多くを混乱させることは間違いありません。

私もそれが早くから採用されていたら良かったのですが、「ifndefが完璧に機能するのになぜそれが必要なのか」という議論を見ることができます。 Cの多くの暗いコーナーと複雑さを考えると、インクルードガードは最も簡単な自己説明的なものの1つです。プリプロセッサがどのように機能するかについて少しでも知っている場合、それらは自明であるべきです。

ただし、大幅なスピードアップが見られる場合は、質問を更新してください。

2
Tim Post

主な違いは、コンパイラがインクルードガードを読み取るためにヘッダーファイルを開く必要があったことです。これに対して、プラグマを使用すると、コンパイラはファイルを追跡し、同じファイルの別のインクルードに遭遇したときにファイルIOを実行しません。それは取るに足らないように聞こえるかもしれませんが、巨大なプロジェクト、特に優れたヘッダーのないプロジェクトでは、規律が簡単に拡大できます。

とはいえ、最近のコンパイラ(GCCを含む)は、プラグマのようなインクルードガードを1回処理するのに十分なほどスマートです。つまり、ファイルを開かず、ファイルIOペナルティを回避します。

プラグマをサポートしていないコンパイラーでは、少し面倒な手動の実装を見てきました。

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

私は個人的には#pragma onceアプローチが好きです。名前付けの衝突や潜在的なタイプミスの手間を回避できるからです。それに比べて、よりエレガントなコードです。ただし、移植可能なコードの場合、コンパイラーが文句を言わない限り、両方を持っていても害はないはずです。

2
Shammi

今日、オールドスクールのインクルードガードは、#pragma onceと同じくらい高速です。コンパイラーがそれらを特別に処理しなくても、#ifndef WHATEVERおよびWHATEVERが定義されると、コンパイラーは停止します。今日、ファイルを開くのは非常に簡単です。たとえ改善があったとしても、ミリ秒のオーダーになります。

利点がないため、#pragmaを1回使用しないでください。他のインクルードガードとの衝突を避けるために、次のようなものを使用します。CI_APP_MODULE_FILE_H-> CI = Company Initials; APP =アプリケーション名。残りは自明です。

2
CMircea

ヘッダーファイルの1回限りの自動インクルードが常に必要であると考えている人々への追加の注意:数十年以来、ヘッダーファイルのダブルまたはマルチインクルードを使用してコードジェネレーターを構築しています。特に、プロトコルライブラリスタブの生成については、追加のツールや言語を使用せずに、非常にポータブルで強力なコードジェネレーターを使用することは非常に快適です。 this blogs X-Macros showとしてこのスキームを使用している開発者は私だけではありません。これは、欠落している自動ガードなしでは実行できません。

1
Marcel

MsvcまたはQt(Qt 4.5まで)を使用する場合、GCC(3.4まで)であるため、msvcは両方とも#pragma onceをサポートするため、#pragma onceを使用しない理由はわかりません。

通常、ソースファイル名は同じクラス名と同じであり、クラス名を変更するにはリファクタリングが必要な場合があることを知っています。その後、#include XXXXも変更する必要があったため、#include xxxxx作業。 Visual Assist X拡張を使用しても、「xxxx」を維持する必要はありません。

1
raidsan