タイトルを参照してください: C++ 関数がインラインであるとはどういう意味ですか?
9 に答える
これは、コンパイラが関数の複数の定義を削除するということを意味します。
通常、関数を複数回定義することはできません(つまり、非インライン関数定義をヘッダーに配置してから、それを複数のコンパイル単位に#includeすると、リンカーエラーが発生します)。関数定義を「インライン」としてマークすると、このエラーが抑制されます(リンカーは、正しいことが確実に行われるようにします)。
それ以上の意味はありません!
最も重要なことは、コンパイラがコンパイルされた関数を各呼び出しサイトに埋め込むという意味ではありません。それが発生するかどうかは完全にコンパイラの気まぐれ次第であり、通常、インライン修飾子はコンパイラの心を変えることはほとんどまたはまったくありません。コンパイラーは、インラインとしてマークされていない関数をインライン化できます。また、インラインとしてマークされている関数に対して関数呼び出しを行うこともできます。
複数の定義を削除することは覚えておくべきことです。
関数は、マクロを使用するのと同様に (概念的に) 呼び出されるのではなく、コードに配置されます。
これにより速度が向上しますが (関数呼び出しがなくなります)、コードが肥大化します (関数が 100 回使用されると、100 個のコピーが作成されます)。
これは、コンパイラが関数をインライン化することを強制するものではないことに注意してください。同様に、コンパイラは通常の関数をインライン化することを決定する場合があります。
これにより、関数全体を cpp ファイルに実装するのではなく、ヘッダー ファイルに配置することもできます (もちろん、その cpp ファイルのみでない限り、インラインで宣言されている場合は未解決の外部を取得するため、これはできません)。使用しました)。
のパフォーマンスへの影響に関する他の(完全に正しい)回答と同様にinline
、C++では、これにより関数をヘッダーに安全に配置できることにも注意してください。
// my_thing.h
inline int do_my_thing(int a, int b) { return a + b; }
// use_my_thing.cpp
#include "my_thing.h"
...
set_do_thing(&do_my_thing);
// use_my_thing_again.cpp
...
set_other_do_thing(&do_my_thing);
これは、コンパイラが、通常の呼び出し可能な関数をコンパイルする必要がある最初のオブジェクト ファイルに、関数の実際の本体のみを含めるためです (通常は、上で示したようにアドレスが取得されるため)。
キーワードがないinline
場合、ほとんどのコンパイラは、MSVC などの複数の定義に関するエラーを返します。
use_my_thing_again.obj : error LNK2005: "int __cdecl do_my_thing(int,int)" (?do_my_thing@@YAHHH@Z) already defined in use_my_thing.obj
<...>\Scratch.exe : fatal error LNK1169: one or more multiply defined symbols found
@老人
コンパイラは、インライン関数としてマークされていないインライン関数のみをインライン化するように要求した場合にのみインライン化します。
「リクエスト」が「最適化をオンにする」ことを意味する場合のみ。
ケースへの影響についてのみ正しい。
どちらでも正解です。
インラインは、リンカーが使用する可能性のある追加情報を生成しません。2 つのオブジェクト ファイルをコンパイルしてチェックします。シンボルがエクスポートされないため、正確に複数の定義が可能です! それが目標だからではありません!
「シンボルはエクスポートされません」とはどういう意味ですか? インライン関数は静的ではありません。それらの名前は表示されます。それらには外部リンケージがあります。C++ 標準から引用するには:
ボイド h(); インライン void h(); // 外部連携
インライン void l(); ボイド l(); // 外部連携
複数の定義がまさに目標です。それは必須です:
インライン関数は、それが使用されるすべての翻訳単位で定義され、すべての場合でまったく同じ定義を持つものとします (3.2)。[注: インライン関数の呼び出しは、その定義が翻訳単位に表示される前に発生する場合があります。] 外部リンケージを持つ関数が 1 つの翻訳単位でインライン宣言されている場合、それが現れるすべての翻訳単位でインライン宣言されなければなりません。診断は必要ありません。外部リンケージを持つインライン関数は、すべての翻訳単位で同じアドレスを持つ必要があります。
関数本体は、文字通り呼び出し元関数内に挿入されます。したがって、この関数を複数回呼び出すと、コードのコピーが複数取得されます。利点は、実行が高速になることです。
通常、関数本体のコピーが、通常の関数呼び出しに対して生成される通常のプロローグ/エピローグ コードよりも大きくない場合、非常に短い関数がインライン化されます。
インラインに関する MSDN の記事で詳細を読むことができます - http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx
インライン関数は、アプリケーションのコード セグメントに配置される命令を生成する可能性があるため、アプリケーションのパフォーマンス プロファイルを変更します。関数をインライン化するかどうかは、コンパイラの裁量です。私の経験では、最新のコンパイラのほとんどは、ユーザーのインライン化要求にいつ応じるべきかを判断するのに優れています。
多くの場合、関数をインライン化するとパフォーマンスが向上します。関数呼び出しには固有のオーバーヘッドがあります。ただし、関数のインライン展開がマイナスになる理由は次のとおりです。
- コードを複製してバイナリ実行可能ファイルのサイズを大きくすると、ディスクのスラッシングが発生し、アプリケーションの速度が低下する可能性があります。
- インライン化されたコードは、アーキテクチャによっては、キャッシュ ミスの原因になったり、キャッシュ ヒットの原因になったりする可能性があります。
C++ FAQ は、キーワードの複雑さをうまく説明しています: http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.3
インラインとしてフラグを立てる関数は、コンパイラーによってインライン化できます。compielrがそれを行うという保証はありません。コンパイラ自体は、複雑なセマンティクスを使用して、デバイスを実行するかどうかを決定します。
関数をインライン化する必要があるとコンパイラーが決定すると、呼び出し元コードでの関数の呼び出しは、caleeのコードに置き換えられます。これは、スタック操作、呼び出し自体を節約し、コードキャッシュの局所性を向上させることを意味します。場合によっては、パフォーマンスが大幅に向上する可能性があります。特に、オブジェクト指向コードで使用されるアクセサーのような1行のデータアクセス関数。
コストは、通常、コードが大きくなり、パフォーマンスが低下する可能性があることです。これが、関数をインラインに設定することがコンパイラにとっての「グリーンフラグ」にすぎず、従う必要がない理由です。コンパイラーは最善を尽くそうとします。
リンケージの特性に対処したくない初心者のための経験則として。インライン関数は、同じコンパイル単位の他の関数によって呼び出されます。複数のコンパイルユニットで使用できるインライン関数を実装する場合は、インライン関数を宣言して実装するヘッダーファイルにします。
なんで?
例:ヘッダーファイルinlinetest.h
int foo();
inline int bar();
コンパイルユニットinlinetest.cppで
int foo(){ int r = bar(); return r; }
inline int bar(){ return 5;};
次に、main.cppで
#include "inlinetest.h"
int main()
{
foo();
//bar();
}
一度に1つのオブジェクトファイルをコンパイルします。その「バー」呼び出しのコメントを外すと、エラーが発生します。インライン関数はinlinetest.oオブジェクトファイルにのみ実装されており、エクスポートされないためです。同時に、foo関数には、bar関数のコードが埋め込まれている可能性が非常に高いです(barは1行であり、I / O操作がないため、インライン化される可能性が非常に高くなります)
ただし、ヘッダーファイルでインライン関数を宣言してインラインで実装した場合は、そのヘッダーを含む任意のコンパイルユニットで使用できます。("コードサンプル");
inlineキーワードを削除すると、mainでbarを呼び出しても、コンパイラーはエラーを引き起こしません。また、コンパイラーにすべての関数をインライン化するように要求しない限り、インライン化は発生しません。これは、ほとんどのコンパイラの標準的な動作ではありません。
非公式には、関数呼び出しがないように、関数の内容を呼び出しサイトに移植することがコンパイラーに許可されていることを意味します。関数に大きな制御ステートメント ( if
、switch
、 など) があり、呼び出しサイトでコンパイル時に条件を評価できる場合 (呼び出しサイトで使用される定数値など)、コードはずっと小さくなります (未使用の枝は落とします)。
より正式には、インライン関数にも異なるリンケージがあります。その点については、C++ の専門家に話してもらいます。
関数を呼び出すと、命令の線形ストリームを持つだけでなく、CPU に特定のパフォーマンス ペナルティが課せられます。CPU のレジスタは別の場所などに書き込む必要があります。明らかに、関数を持つことの利点は、通常、パフォーマンスの低下を上回ります。ただし、パフォーマンスが問題になる場合、たとえば、伝説的な「内部ループ」関数やその他のボトルネックの場合、コンパイラは、関数を呼び出すために CPU の税金を処理する代わりに、関数のマシン コードを実行のメイン ストリームに挿入できます。 .