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


311

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

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

プラットフォームのコンパイルの問題を避けたいだけでなく、フォールバックガードの余分な作業も避けたいです。

#pragma once
#ifndef HEADER_H
#define HEADER_H

...

#endif // HEADER_H

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


3
同様の質問をした後、私#pragma onceはVS 2008のクラスビューの問題をいくつか回避しているようだと気付きました。私は#pragma onceこの理由で、インクルードガードを取り除き、それらをすべて置き換えるプロセスにいます。
SmacL

回答:


189

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

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


12
とにかく遅くなるべきではありません(とにかくGCCで)。
Jason Coco

54
そのように実装されていません。代わりに、ファイルが最初に#ifndefで始まり、#endifで終わる場合、gccはそれを記憶し、ファイルを開かなくても、将来そのインクルードを常にスキップします。
Jason Coco

10
#pragma onceファイルは前処理されていないため、一般的に高速です。ifndef/define/endifとにかく前処理が必要です。これは、このブロックの後に(理論的には)コンパイル可能なものがあるためです
Andrey

13
ガードマクロの最適化に関するGCCドキュメント:gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html
Adrian

38
インクルードガードを使用するには#ifndef FOO_BAR_H、通常「foo_bar.h」などのファイルに対して、などの新しいシンボルを定義する必要があるという追加の要件があります。後でこのファイルの名前を変更する場合、この規則に一致するようにインクルードガードを適宜調整する必要がありますか?また、コードツリーの2つの異なる場所に2つの異なるfoo_bar.hがある場合、それぞれに対して2つの異なるシンボルを考える必要があります。短い答えは、使用することで#pragma onceあり、本当にそれをサポートしない環境でコンパイルする必要がある場合は、先に進んで、その環境用のインクルードガードを追加します。
ブランディン2014年

329

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


36
しかし、別の#define NAMESを作成する必要なしに、同じ場所に同じ名前の2つのファイルを置くこともできます。これは、HEADERFILENAME_Hの形式で行われることが多い
Vargas

69
同じ#define WHATEVERを使用して2つ以上のファイルを作成することもできます。これにより、楽しみの終わりがなくなります。そのため、プラグマを1回使用することをお勧めします。
Chris Huang-Leaver

107
説得力がない...ビルドシステムを、ファイルをコピーせずにシンボリックリンクを使用するものに変更するか、各翻訳単位の1つの場所からのみ同じファイルを含めます。インフラストラクチャは混乱しているため、再編成する必要があります。
Yakov Galka

3
また、異なるディレクトリに同じ名前の異なるファイルがある場合、#ifdefアプローチはそれらを同じファイルと見なします。したがって、一方には欠点があり、もう一方には欠点があります。
rxantos 14

3
@rxantos、ファイルが異なる場合、#ifdefマクロ値も異なる場合があります。
Motti、2014

63

私は望む #pragma once(またはそのようなもの)が標準にあっます。インクルードガードはそれほど大きな問題ではありません(ただし、言語を学習している人に説明するのは少し難しいようです)が、回避できた可能性のある軽度の煩わしさのようです。

実際、99.98%の時から、#pragma once動作は望ましい動作です。ヘッダーの複数インクルードの防止がコンパイラによって自動的に処理された場合、それは良かったでしょう。#pragmaダブルを許可場合に適しています。

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


48
私が本当に望んでいるのは、標準的な#import指令です。
John

10
標準のインポートディレクティブがリリースさ れます:isocpp.org/blog/2012/11/…しかし、まだここにはありません。私はそれを強く支持しています。
AHelps、2015

6
@AHelps Vaporware。もう5年になりますか?たぶん2023年にあなたはこのコメントに戻って「私はそう言った」と言うでしょう。
doug65536 16

これはベーパーウェアではありませんが、技術仕様の段階にあります。モジュールはVisual Studio 2015(blogs.msdn.microsoft.com/vcblog/2015/12/03/…)とclang(clang.llvm.org/docs/Modules.html)に実装されています。そして、それは#importではなくインポートです。
AHelps 2016

C ++ 20にする必要があります。
イオノクラストブリガム2017

36

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

#ifndef HEADERNAME_H
#define HEADERNAME_H
...
#endif

同じ処理を行い、プリプロセッサに追加のマクロを設定しません。

GCC はバージョン3.4から#pragma once正式にサポートしています。


25

GCCは#pragma once3.4以降でサポートされています。コンパイラのサポートについては、http://en.wikipedia.org/wiki/Pragma_onceを参照してください

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

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

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


11

私はそれを使用しており、新しいヘッダーを作成するために入力する必要がほとんどないので、満足しています。Windows、Mac、Linuxの3つのプラットフォームで、私には問題なく動作しました。

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

結局のところ、ガードやプラグマを使用することはまったく問題ではありません。


#pragmaは一度は嫌いですが、相対的な利点を指摘していただきありがとうございます。「通常の」動作環境では、C ++解析は他のものよりもはるかに高価です。コンパイル時間が問題になる場合は、リモートファイルシステムからコンパイルする人はいません。
トム・

1
C#と比較してC ++の解析が遅い。C#では、小さなC ++ファイルごとに何千ものLOCヘッダーファイル(iostreamなど)を(文字通り)解析する必要はありません。ただし、この問題を小さくするには、プリコンパイル済みヘッダーを使用してください
Eli Bendersky

11

' #pragma once' を使用しても効果がない可能性があります(どこでもサポートされているわけではありませんが、ますます広くサポートされています)。そのため、とにかく条件付きコンパイルコードを使用する必要があり#pragma onceます。コンパイラはおそらくとにかくそれを最適化します。ただし、ターゲットプラットフォームによって異なります。すべてのターゲットがそれをサポートしている場合は、先に進んでそれを使用してください。ただし、プラグマのみを使用し、それをサポートしていないコンパイラーに移植した場合、すべての問題が解消されるため、意識的に決定する必要があります。


1
とにかく警備員をサポートしなければならないことに同意しません。#pragmaを1回(またはガード)使用する場合、これはそれらなしでいくつかの競合を引き起こすためです。したがって、チェーンツールでサポートされていない場合、プロジェクトはコンパイルされず、古いK&Rコンパイラでansi Cをコンパイルする場合とまったく同じ状況になります。いくつかのガードを追加するには、最新のチェーンツールを入手するか、コードを変更する必要があります。プログラムがコンパイルされていても動作しなかった場合、すべての問題が発生します。
2016

5

パフォーマンス上の利点は、#pragmaが一度読み込まれると、ファイルを再度開く必要がないことです。ガードを使用すると、コンパイラーはファイルを開いて(時間がかかる可能性があります)、コンテンツを再び含めてはならないという情報を取得する必要があります。

一部のコンパイラは、コンパイル単位ごとに、読み込まれたコードが含まれていないファイルを自動的に開かないため、これは理論にすぎません。

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

最後に、この場合、各コンパイラーの動作を確認する必要なしにコンパイラーから最高の速度を確実に得ることができるより良い提案は、プラグマとガードの両方を使用することです。

#ifndef NR_TEST_H
#define NR_TEST_H
#pragma once

#include "Thing.h"

namespace MyApp
{
 // ...
}

#endif

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

タイプするのが長いので、私は個人的にツールを使用して、すべてを非常に素晴らしい方法で生成します(Visual Assist X)。


Visual Studioは#includeガードをそのまま最適化しませんか?他の(より良い?)コンパイラーはそうします。
Tom

1
なぜあなたはpragma後に置くのifndefですか?メリットはありますか?
user1095108

1
@ user1095108一部のコンパイラは、ヘッダーガードを区切り文字として使用して、ファイルに一度インスタンス化する必要のあるコードのみが含まれているかどうかを確認します。一部のコードがヘッダーガードの外にある場合、ファイル全体が複数回インスタンス化可能であると見なされる可能性があります。同じコンパイラーがプラグマを一度サポートしない場合、その命令は無視されます。したがって、プラグマをヘッダーガード内に1回置くのが、少なくともヘッダーガードを「最適化」できるようにするための最も一般的な方法です。
クライム

4

常にではない。

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


10
それはコンパイラのバグでしょう。(必要のないショートカットを取得しようとしています)。
rxantos 14

4
#pragma onceは非標準であるため、コンパイラが実行を決定するものはすべて「正しい」ものです。もちろん、「期待される」ものと「有用な」ものについて話し始めることができます。
user7610

2

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

実際の節約はないので、gccの古いバージョンや他のコンパイラさえも混乱させる可能性があるのか​​、実際にはわかりません。私はさまざまなde-linterのすべてを試したことはありませんが、それらの多くを混乱させると確信しています。

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

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


2

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

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


19
タイピングがはるかに少ないという利点はありませんか?
Andrey

1
ただし、10万回の数ミリ秒は数分です。大規模なプロジェクトは、それぞれ数十のヘッダーを含む1万のファイルで構成されます。現在のメニーコアCPUを考えると、入出力、特に多数の小さなファイルを開くことは、主要なボトルネックの1つです。
デイモン

1
「今日のオールドスクールインクルードガードは、#pragmaと同じくらい高速です。」今日、そして何年も前に。GCCサイトの最も古いドキュメントは2001年の2.95のものであり、インクルードガードの最適化は当時としては新しいものではありませんでした。最近の最適化ではありません。
Jonathan Wakely、2015年

4
主な利点は、ガードを含めるとエラーが発生しやすく、冗長になることです。同じ名前の2つの異なるファイルを異なるディレクトリに配置する(インクルードガードが同じシンボルである可能性がある)ことや、インクルードガードのコピー中にコピーアンドペーストエラーを発生させるのは簡単です。Pragma onceはエラーが発生しにくく、すべての主要なPCプラットフォームで動作します。使えればスタイルもいいです。
AHelps、2015

2

主な違いは、コンパイラがインクルードガードを読み取るためにヘッダーファイルを開かなければならないことです。対照的に、プラグマは一度、コンパイラーがファイルを追跡し、同じファイルの別のインクルードに遭遇したときにファイルIOを実行しません。それは無視できるように聞こえるかもしれませんが、それは巨大なプロジェクトで簡単にスケールアップできます。

とは言っても、最近のコンパイラー(GCCを含む)は、プラグマのようなインクルードガードを一度だけ扱うのに十分スマートです。つまり、ファイルを開かず、ファイルのIOペナルティを回避します。

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

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

#pragmaは、名前の衝突や潜在的な入力ミスの煩わしさを回避できるので、個人的には一度アプローチするのが好きです。比較すると、よりエレガントなコードでもあります。とは言っても、移植可能なコードの場合、コンパイラーがそれについて文句を言わない限り、両方を持つことは害になりません。


1
「とは言え、最近のコンパイラー(GCCを含む)は、プラグマのようなインクルードガードを1度だけ扱うのに十分スマートです。」彼らは何十年もそれをやっており、おそらく#pragma once存在するよりも長いでしょう!
Jonathan Wakely 2015年

あなたは私を誤解したと思います。プラグマの前に一度言うと、すべてのコンパイラーは、プリプロセッサー段階で複数回含まれる同じhファイルに対して複数のIOペナティーを被るでしょう。最近の実装では、最終的にプリプロセッサステージでファイルのキャッシュが改善されています。いずれにせよ、プラグマなしでは、プリプロセッサーステージには、インクルードガードの外にあるすべてが含まれます。プラグマを1回使用すると、ファイル全体が除外されます。その観点から、プラグマは依然として有利です。
シャミ2015年

1
いいえ、それは間違っています。まともなコンパイラーは、#pragmaがなくてもファイル全体を除外します。2度目はファイルを開かず、2度目も見ません。gcc.gnu.org/ onlinedocs /を参照してください。 cpp / Once-Only-Headers.html(これはキャッシングとは関係ありません)。
Jonathan Wakely

1
あなたのリンクから、最適化はcppでのみ行われるようです。とにかく、キャッシングが機能します。プリプロセッサは、ガードの外にコードを含めることをどのようにして知っていますか 例... extern int foo; #ifndef INC_GUARD #define INC_GUARD class ClassInHeader {}; #endifこの場合、プリプロセッサにはextern int fooを含める必要があります。同じファイルを複数回インクルードする場合は複数回。1日の終わり、#pragma onceとincludeガードの違いを理解し、さまざまなコンパイラがそれらの両方でどのように動作するかを理解する限り、これについて議論することはあまりありません:)
Shammi

1
もちろん、最適化は適用されません。
Jonathan Wakely、2015年

1

msvcまたはQt(最大Qt 4.5)を使用する場合、GCC(最大3.4)、msvcの両方がサポートされているため#pragma once、を使用しない理由はわかりません#pragma once

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


1

ヘッダーファイルの自動1回のみのインクルードが常に望ましいと考える人々への追加の注意:数十年以来、ヘッダーファイルの二重または複数のインクルードを使用してコードジェネレーターをビルドしています。特にプロトコルライブラリスタブの生成については、ツールや言語を追加することなく、非常にポータブルで強力なコードジェネレーターを使用するのが非常に快適です。このブログのX-Macrosが示すようにこのスキームを使用しているのは私だけではありません。これは、自動ガードが欠落していなければ不可能です。


C ++テンプレートで問題を解決できますか?C ++テンプレートの方法が原因で、マクロの有効なニーズを見つけることはめったにありません。
17

1
私たちの長期的な専門的経験は、成熟した言語、ソフトウェア、およびツールインフラストラクチャを常に使用することで、サービスプロバイダー(組み込みシステム)として、生産性と柔軟性に大きな利点をもたらすということです。代わりに、C ++ベースの組み込みシステムソフトウェアとスタックを開発している競合他社は、一部の開発者が仕事に満足していると感じるかもしれません。しかし、私たちは通常、市場投入までの時間、機能性、および柔軟性の面で何回も上回っています。ネザーは、同じツールを何度も繰り返し使用することで得られる生産性の向上を過小評価しています。代わりに、Web-Devsは多くのフレームワークへの方法に苦しんでいます。
マルセル

ただし、DRYの原則自体に対して、すべてのヘッダーファイルにguards /#pragmaが1回含まれていません。私はあなたのポイントをX-Macro機能で見ることができますが、それはインクルードの主な用途ではありません、私たちがDRYにこだわるのであれば、それはheader unguard /#pragma multiのような他の方法ではないでしょうか?
caiohamamura

DRYは「Dont repeat YOURSELF」の略です。それは人間についてです。マシンが行っていることは、そのパラダイムとは何の関係もありません。C ++テンプレートは何度も繰り返され、Cコンパイラもそれを繰り返します(たとえば、ループのアンロール)。すべてのコンピュータは、信じられないほどのほとんどすべてを頻繁かつ高速に繰り返します。
マルセル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.