ヘッダーのみのライブラリを作成していますが、ユーザーに提供する関数を宣言するstatic
か、inline
. その場合、どちらか一方を優先する必要がある理由はありますか?
5 に答える
それらは両方とも異なる機能を提供します。
inline
キーワード(§7.1.3 / 4)を使用することには2つの意味があります。
- これは、通常の関数呼び出しメカニズムよりも、呼び出し時点での関数本体の置換が望ましいことをコンパイラーに示唆しています。
- インライン置換が省略されている場合でも、インラインの他のルール(特に1つの定義ルール)に従います。
関数のstatic
キーワードは、関数inline
に内部リンケージを強制します(インライン関数には外部リンケージがあります)。このような関数の各インスタンスは個別の関数として扱われ(各関数のアドレスは異なります)、これらの関数の各インスタンスには独自のコピーがあります。静的ローカル変数と文字列リテラルの数(インライン関数にはこれらのコピーが1つだけあります)
注: この回答は C++ 用です。C については、caf の回答を参照してください。2 つの言語は異なります。
static
次の 2 つの関連する意味があります。
名前空間スコープの関数の場合、関数に内部リンケージ
static
を与えます。これは、実際には、名前がリンカから見えないことを意味します。もこの方法でデータに使用できますが、データの場合、この使用法は C++03 で廃止されました (C++03 Annex D の§D.2、規範)。それでも、定数にはデフォルトで内部リンケージがあります (データの使用は非推奨であるため、明示的にすることはお勧めできません)。static
クラス内の関数の場合、クラスのオブジェクトなしで関数を呼び出せる
static
ように、暗黙のthis
引数を削除します。
ヘッダーでは、通常、関数の内部リンケージを使用しません。これは、ヘッダーが含まれるすべてのコンパイル単位で関数を複製したくないためです。
detail
public モジュール インターフェイスの一部ではないクラスまたは関数が必要で、通常の名前空間の汚染を減らしたい場合 (つまり、名前の競合の可能性を減らしたい場合) は、代わりに というネストされた名前空間を使用するのが一般的な規則です。この規則は、Boost ライブラリによって使用されます。インクルード ガード シンボルと同様に、この規則は現在の C++ でのモジュール サポートの欠如を意味します。基本的には、規則を介していくつかの重要な言語機能をシミュレートすることになります。
この言葉inline
には、次の 2 つの関連する意味もあります。
名前空間スコープの関数の場合、関数の定義が使用されるすべてのコンパイル単位で意図的に提供されることをコンパイラに伝えます。実際には、これによりリンカーは関数の複数の定義を無視し、ヘッダー ファイルで非テンプレート関数を定義できるようになります。
inline
テンプレートを使用してデータの効果をシミュレートすることはできますが、データに対応する言語機能はありません。また、残念ながら、関数の呼び出しは機械語コードで「インライン」に展開する必要があるという強いヒントがコンパイラに与えられます。
最初の意味は、 の唯一の保証された意味ですinline
。
一般inline
に、ヘッダー ファイル内のすべての関数定義に適用します。例外が 1 つあります。つまり、クラス定義で直接定義された関数です。そのような関数は自動的に宣言されinline
ます (つまり、単語 を明示的に追加せずにリンカーのプロテストを避けるためinline
、このコンテキストでの実用的な使用法の 1 つはinline
、クラス内の関数宣言に適用して、その関数の定義が後で続くことを読者に伝えることです)。ヘッダー ファイル内)。
static
したがって、 andの意味について少し混乱しているように見えますが、inline
それらは互換性がありません。– あなたは基本的にその通りstatic
で、inline
何らかの形でつながっています。(フリー) 関数をクラスから名前空間スコープに移動すると、 static
→inline
に変更され、関数を名前空間スコープからクラスに移動すると、inline
→に変更されますstatic
。これは一般的なことではありませんが、ヘッダーのみのコードをリファクタリングしているときに珍しいことではないことがわかりました。
要約:
inline
ヘッダーで定義されたすべての名前空間スコープ関数に使用します。特に、inline
関数テンプレートの特殊化には使用してください。関数テンプレートは完全に特殊化することしかできず、完全な特殊化は通常の関数であるためです。inline
ヘッダー ファイルで関数テンプレートの特殊化を適用しないと、一般にリンク エラーが発生します。detail
内部実装の詳細名による汚染を避けるために呼び出されるなど、特別なネストされた名前空間を使用します。static
静的クラス メンバーに使用します。static
を使用して、定数に内部リンケージがあることを明示しないでください。この使用static
は C++03 では非推奨になっているためです (非推奨は C++11 で削除されたようですが)。inline
データに適用することはできませんが、テンプレートを使用することでほぼ同じ (実際の) 効果を達成できることに注意してください。ただし、ヘッダーに実装された共有定数データの大きな塊が必要な場合は、inline
関数を介してデータへの参照を作成することをお勧めします。コードを作成するのははるかに簡単で、コードの読者にとっては理解しやすいものです。:-)
static
とinline
は直交しています。言い換えれば、あなたはどちらかまたは両方を持つことができます。それぞれに独自の用途があり、それらを使用するかどうかを決定します。
その関数がクラス状態を共有していない場合:static
。関数を作成することstatic
は、どこからでも呼び出されることで恩恵を受けることができます。
関数が比較的小さく明確な場合:inline
(この回答は C99 ルールに基づいています。質問は C と C++ の両方でタグ付けされており、C++ のルールは異なる可能性があります)
関数を just として宣言すると、関数inline
の定義はインライン定義になります。コンパイラは、翻訳単位内の関数へのすべての呼び出しに対してインライン定義を使用する必要はありません。別の翻訳単位で提供された同じ関数の外部定義があると想定し、一部またはすべての呼び出しにそれを使用することが許可されています。 . ヘッダーのみのライブラリの場合、そのような外部定義は存在しないため、それを使用するプログラムは、リンク時に定義が欠落して失敗する可能性があります。
一方、関数を と の両方として宣言することもできinline
ます static
。この場合、コンパイラは、実際にそれらをインライン化するかどうかにかかわらず、翻訳単位内の関数へのすべての呼び出しに対して提供された定義を使用する必要があります。これは、ヘッダーのみのライブラリに適しています (ただし、コンパイラは、宣言された関数のみと宣言inline static
された関数に対してまったく同じように動作する可能性がありますstatic
が、どちらの場合も、有益だと思われる場合はインライン化するため、実際にはおそらくほとんどのみで得られますstatic
)。