inline
C ++の関数/メソッドのキーワードはいつ書くべきですか?
いくつかの回答、いくつかの関連する質問を確認した後:
C ++で関数/メソッドのキーワード「インライン」を記述すべきでないのはいつですか?
関数/メソッドを「インライン」にするタイミングをコンパイラが知らないのはいつですか?
関数/メソッドの「インライン」を書き込むときにアプリケーションがマルチスレッド化されているかどうかは重要ですか?
inline
(9.3 / 2)。
inline
C ++の関数/メソッドのキーワードはいつ書くべきですか?
いくつかの回答、いくつかの関連する質問を確認した後:
C ++で関数/メソッドのキーワード「インライン」を記述すべきでないのはいつですか?
関数/メソッドを「インライン」にするタイミングをコンパイラが知らないのはいつですか?
関数/メソッドの「インライン」を書き込むときにアプリケーションがマルチスレッド化されているかどうかは重要ですか?
inline
(9.3 / 2)。
回答:
ああ、私のペットのおしっこの一つ。
inline
以上のようなものですstatic
か、extern
あなたの関数をインライン化するコンパイラを伝えるディレクティブより。 extern
、static
、inline
リンカではなく、コンパイラによってほぼ独占的に使用するリンケージ・ディレクティブは、あります。
inline
関数をインライン化する必要があるとコンパイラに示唆していると言われています。それは1998年には本当だったかもしれませんが、10年後、コンパイラーはそのようなヒントを必要としません。コードの最適化に関しては、人間は通常間違っていることは言うまでもありません。そのため、ほとんどのコンパイラは「ヒント」を無視します。
static
-変数/関数名は他の翻訳単位では使用できません。リンカーは、静的に定義された変数/関数を別の翻訳単位から誤って使用しないようにする必要があります。
extern
-この翻訳単位でこの変数/関数名を使用しますが、定義されていなくても文句を言わないでください。リンカはそれを整理し、いくつかの外部シンボルを使用しようとしたすべてのコードがそのアドレスを持っていることを確認します。
inline
-この関数は複数の翻訳単位で定義されますので、心配しないでください。リンカは、すべての変換ユニットが変数/関数の単一のインスタンスを使用することを確認する必要があります。
注:テンプレートにinline
はinline
すでにリンケージセマンティクスがあるため、通常、テンプレートの宣言は無意味です。ただし、テンプレートの明示的な特殊化とインスタンス化を使用する必要inline
があります。
質問に対する具体的な回答:
C ++で関数/メソッドのキーワード「インライン」を書く必要があるのはいつですか?
ヘッダーで関数を定義する場合のみ。より正確には、関数の定義が複数の翻訳単位で表示される場合のみです。コードを最適化する際にコンパイラーが処理するためのより多くの情報を提供するので、ヘッダーファイルで小さな(1つのライナーのような)関数を定義することをお勧めします。また、コンパイル時間が長くなります。
C ++で関数/メソッドのキーワード「インライン」を記述すべきでないのはいつですか?
コンパイラーがインライン化するとコードがより高速に実行されると思うからといって、インラインを追加しないでください。
関数/メソッドを「インライン」にするタイミングをコンパイラが知らないのはいつですか?
一般に、コンパイラーはあなたよりもこれをうまく行うことができます。ただし、関数定義がない場合、コンパイラーにはコードをインライン化するオプションがありません。最大限に最適化されたコードでは、通常、private
要求するかどうかに関係なく、すべてのメソッドがインライン化されます。
GCCではインライン化を回避するための余地として、を使用し__attribute__(( noinline ))
、Visual Studioではを使用します__declspec(noinline)
。
関数/メソッドの「インライン」を書き込むときにアプリケーションがマルチスレッド化されているかどうかは重要ですか?
マルチスレッド化は、インライン化にはまったく影響しません。
inline
キーワードが関連していないということでした。しかし、あなたは正しい考えを持っています。原則として、インライン化によって何が改善されるかを推測すると、エラーが発生しやすくなります。その規則の例外はワンライナーです。
このスレッドのすばらしい答えすべてに貢献し、残りの誤解を分散させる説得力のある例を示したいと思います。
次のような2つのソースファイルがあるとします。
inline111.cpp:
#include <iostream>
void bar();
inline int fun() {
return 111;
}
int main() {
std::cout << "inline111: fun() = " << fun() << ", &fun = " << (void*) &fun;
bar();
}
inline222.cpp:
#include <iostream>
inline int fun() {
return 222;
}
void bar() {
std::cout << "inline222: fun() = " << fun() << ", &fun = " << (void*) &fun;
}
ケースA:
コンパイル:
g++ -std=c++11 inline111.cpp inline222.cpp
出力:
inline111: fun() = 111, &fun = 0x4029a0
inline222: fun() = 111, &fun = 0x4029a0
議論:
インライン関数の定義が同じであっても、C ++コンパイラはそうでない場合はフラグを立てません(実際には、個別のコンパイルのため、チェックする方法がありません)。これを確実にするのはあなた自身の義務です!
として宣言されているように、リンカーは1つの定義ルールについて文句を言いません。しかし、inline111.cppは最初の翻訳単位(実際に呼び出しているコンパイラによって処理)、コンパイラはインスタンス化し、その時に最初でコール出会いinline111.cpp。コンパイラがプログラムの他の場所(たとえばinline222.cppから)からの呼び出しを拡張しないと決定した場合、への呼び出しは常にinline111.cppから生成されたインスタンス(inline222.cpp内への呼び出し)にリンクされます。fun()
inline
fun()
fun()
fun()
fun()
fun()
その翻訳単位でインスタンスが生成されることもありますが、リンクは解除されたままになります)。確かに、それは同じ&fun = 0x4029a0
プリントアウトから明らかです。
最後に、実際にone-linerを拡張するinline
ようにコンパイラーに提案しているにもかかわらず、両方の行にあるためfun()
、それはあなたの提案を完全に無視しますfun() = 111
。
ケースB:
コンパイル (逆順の通知):
g++ -std=c++11 inline222.cpp inline111.cpp
出力:
inline111: fun() = 222, &fun = 0x402980
inline222: fun() = 222, &fun = 0x402980
議論:
このケースは、ケースAで議論されたことを主張します。
あなたが実際の呼び出しをコメントアウトした場合という、重要な点に注意してくださいfun()
にinline222.cpp(例えばアウトコメントcout
で-statement inline222.cppあなたの翻訳単位のコンパイル順序にもかかわらず、その後、完全に)fun()
それが最初の呼び出しの出会いだ時にインスタンス化されますinline111.cpp。結果として、ケースBはとして出力されinline111: fun() = 111, &fun = 0x402980
ます。
ケースC:
コンパイル (通知-O2):
g++ -std=c++11 -O2 inline222.cpp inline111.cpp
または
g++ -std=c++11 -O2 inline111.cpp inline222.cpp
出力:
inline111: fun() = 111, &fun = 0x402900
inline222: fun() = 222, &fun = 0x402900
議論:
-O2
最適化をするために、コンパイラを奨励し、実際に展開する(つまりもお知らせインライン化することが可能な機能を-fno-inline
あるデフォルトの最適化オプションなし)。ここでのアウトプリントから明らかなように、fun()
は実際にはインライン展開されており(その特定の翻訳単位での定義に従って)、2つの異なる fun()
プリントアウトになります。これにもかかわらず、同一のプリントアウトから明らかなように、(規格で要求されているように)グローバルにリンクされたインスタンスはまだ1つしかありません。fun()
&fun
inline
関数を未定義の動作にする理由を説明する投稿です。
.cpp
が独自の翻訳単位であるケースも追加する必要があります。できれば、-flto
有効/無効のケースを追加してください。
1)今日では、ほとんどありません。関数をインライン化することをお勧めしますが、コンパイラはあなたの助けなしにそれを行います。
2)常に。#1を参照してください。
(あなたがあなたの質問を2つの質問に分けたことを反映するように編集されました...)
inline
は、たとえば、ヘッダーファイルで関数を定義するために必要です(そのような関数をいくつかのコンパイルユニットでインライン化するために必要です)。
inline
指定子がある場合、そのインスタンスはリンカーによって自動的に1つに折りたたまれ、ODRは使用されません。
C ++で関数/メソッドのキーワード「インライン」を記述すべきでないのはいつですか?
関数がヘッダーで宣言され、.cpp
ファイルで定義されている場合は、キーワードを記述しないでください。
コンパイラーは、関数/メソッドを「インライン」にするタイミングをいつ知っているのですか?
そのような状況はありません。コンパイラーは関数をインライン化できません。関数の一部またはすべての呼び出しをインライン化するだけで実行できます。関数のコードを取得していない場合は実行できません(その場合、リンカーは、実行できる場合は実行する必要があります)。
関数/メソッドの「インライン」を書き込むときにアプリケーションがマルチスレッド化されているかどうかは重要ですか?
いいえ、それはまったく問題ではありません。
これは、使用するコンパイラによって異なります。今日のコンパイラーはインライン化の方法を人間よりよく知っていることを盲目的に信じないでください。最適化のヒントではなくリンケージディレクティブであるため、パフォーマンス上の理由から使用しないでください。これらの議論はイデオロギー的に正しいことに同意しますが、現実に正しく遭遇することは別のことかもしれません。
複数のスレッドを読んだ後、好奇心から私が作業しているコードへのインラインの影響を試しましたが、結果として、GCCでは測定可能なスピードアップが得られ、Intelコンパイラではスピードアップされませんでした。
(詳細:クラス外で定義された重要な関数がほとんどない数学シミュレーション、GCC 4.6.3(g ++ -O3)、ICC 13.1.0(icpc -O3);重要なポイントにインラインを追加すると、GCCコードで+ 6%スピードアップしました)。
したがって、GCC 4.6を最新のコンパイラとして認定した場合、CPU集中型のタスクを記述してボトルネックがどこにあるかを正確に把握している場合、インラインディレクティブは依然として重要です。
実際には、ほとんどありません。あなたがしていることは、コンパイラが特定の関数をインラインにすることを提案することです(たとえば、この関数へのすべての呼び出し/ w本体を置き換える)。もちろん、保証はありません。コンパイラはディレクティブを無視する場合があります。
コンパイラーは、通常、このようなことを検出して最適化します。
inline
持っているセマンティック C ++(例えば複数の定義が扱われる方法で)いくつかのケースでは重要であり、(例えばテンプレート)の違いを。
最適化を有効にせずにコンパイルすると、デフォルトではgccは関数をインライン化しません。ビジュアルスタジオについて知らない– deft_code
/ FAcsを使用してコンパイルし、アセンブリコードを確認することで、Visual Studio 9(15.00.30729.01)についてこれを確認しました。コンパイラーは、デバッグモードで最適化を有効にせずにメンバー関数の呼び出しを生成しました。関数が__forceinlineでマークされていても、インラインランタイムコードは生成されません。
戻り値の型の前に、最初に入れたいと思います。しかし、ほとんどのコンパイラはそれを無視します。それが定義されていて、コードのブロックが小さい場合、ほとんどのコンパイラはとにかくインラインであると見なします。
ライブラリを作成している場合や特別な理由がない限り、代わりにリンク時の最適化を忘れてinline
使用することができます。これは、コンパイル単位全体でインライン化するために関数定義がヘッダーに含まれている必要があるという要件を取り除きます。これは、まさに許可されていることです。inline
(ただし、リンク時最適化を使用しない理由はありますか?)
インラインキーワードは、関数呼び出しを関数の本文で置き換えるようにコンパイラーに要求し、最初に式を評価してから渡します。戻りアドレスを格納する必要がなく、関数にスタックメモリが必要ないため、関数呼び出しのオーバーヘッドが削減されます。引数。
いつ使用するか:
- パフォーマンスを改善するには
- 通話のオーバーヘッドを減らすため。
- コンパイラへのリクエストにすぎないため、特定の関数はインライン化されません*大きな関数
- 条件付き引数が多すぎる関数
- 再帰的なコードとループなどのコード
inline
、CおよびC ++で使用するかどうかに関係なく、関数をインライン化しません。Cインライン:stackoverflow.com/a/62287072/7194773 C ++インライン: stackoverflow.com/a/62230963/7194773
C ++インラインはとは全く異なっているCインライン。
#include <iostream>
extern inline int i[];
int i [5];
struct c {
int function (){return 1;} //implicitly inline
static inline int j = 3; //explicitly inline
};
int main() {
c j;
std::cout << i;
}
inline
それ自体がコンパイラ、アセンブラ、リンカに影響します。これは、翻訳単位で使用されている場合、この関数/データのシンボルのみを発行し、クラスメソッドのように、セクション.section .text.c::function(),"axG",@progbits,c::function(),comdat
または.section .bss.i,"awG",@nobits,i,comdat
データにそれらを格納するようにアセンブラーに指示することを示すコンパイラへのディレクティブです。テンプレートのインスタンス化は、独自のcomdatグループにも含まれます。
これは続く.section name, "flags"MG, @type, entsize, GroupName[, linkage]
。たとえば、セクション名は.text.c::function()
です。axG
セクションが割り当て可能で実行可能で、グループ内にあることを意味します。つまり、グループ名が指定されます(Mフラグがないため、entsizeは指定されません)。@progbits
セクションにデータが含まれ、空白ではないことを意味します。c::function()
グループ名であり、グループはcomdat
リンケージは、すべてのオブジェクトファイルで、comdatでタグ付けされたこのグループ名で遭遇するすべてのセクションが1を除いて最終的な実行可能ファイルから削除されることを意味します。オブジェクトファイル内の独自のグループ(1つのグループの1つのセクション)にある場合、リンカーは、オブジェクトファイルに同じ名前のグループがある場合、最後の.exeに1つだけ含めるようにします。差inline
と使用していないがinline
、それが規則的に格納されていないため、リンカーアセンブラに、その結果として、今表示されている.data
か、.text
それらの指令にアセンブラによってなど。
static inline
クラス内では、これは宣言ではなく型定義であり(静的メンバーをクラスで定義できます)、インラインにします。これで、上記のように動作します。
static inline
atファイルスコープはコンパイラにのみ影響します。これは、コンパイラにとって意味があります。変換ユニットで使用されている場合にのみ、この関数/データのシンボルを発行し、通常の静的シンボルとして発行します(.globlディレクティブなしでin.text /.dataを格納)。アセンブラにとって、との間に違いはstatic
ありませんstatic inline
extern inline
この宣言は、このシンボルを翻訳単位で定義するか、コンパイラエラーをスローする必要があることを意味します。それが定義されていた場合、その後の正規として扱いinline
、アセンブラ、リンカにそこの間に違いないであろうextern inline
とinline
、そう、これが唯一のコンパイラのガードです。
extern inline int i[];
extern int i[]; //allowed repetition of declaration with incomplete type, inherits inline property
extern int i[5]; //declaration now has complete type
extern int i[5]; //allowed redeclaration if it is the same complete type or has not yet been completed
extern int i[6]; //error, redeclaration with different complete type
int i[5]; //definition, must have complete type and same complete type as the declaration if there is a declaration with a complete type
エラー行のない上記の全体がに縮小されinline int i[5]
ます。もちろん、割り当てによる明示的な定義のため、そうしたextern inline int i[] = {5};
場合extern
は無視されます。
コードを開発およびデバッグするときは、省略inline
してください。デバッグが複雑になります。
それらを追加する主な理由は、生成されたコードを最適化するためです。通常、これはコードスペースの増加と速度をトレードオフしますがinline
、コードスペースと実行時間の両方を節約することもあります。
アルゴリズムの完了前にこのようなパフォーマンスの最適化について考えることは、時期尚早の最適化です。
inline
関数は通常、最適化でコンパイルしない限りインライン化されないため、デバッグに影響を与えることはありません。これはヒントであり、要求ではないことを忘れないでください。
inline
機能はインライン化された。それらに意味のあるブレークポイントを設定することは不可能でした。
inline
しても、それ自体でインライン化するかどうかを判断できる最新のコンパイラーでコードを改善することはできません。
インライン化する必要がある場合:
1.パラメータの受け渡し、コントロールの転送、コントロールの戻りなどのように、関数が呼び出されたときに発生するオーバーヘッドを回避したい場合。
2.関数は小さく、頻繁に呼び出される必要があり、80-20ルールに従って、プログラムのパフォーマンスに大きな影響を与える関数をインラインで作成しようとするため、インラインで作成することは非常に有利です。
私たちが知っているように、インラインはレジスターに似たコンパイラーへの要求にすぎず、オブジェクトコードサイズでコストがかかります。
inline
最適化のヒントとしてのステータスを失いました。ほとんどのコンパイラは、それを複数の定義を許可するためにのみ使用します-IMOのように。さらに、C ++ 11以降register
、「コンパイラーよりも最適化の方法をよく知っている」という以前の意味が完全に廃止されています。これは、現在の意味を持たない予約語にすぎません。
inline
ある程度耳を傾けています。
C ++インライン関数は、クラスで一般的に使用される強力な概念です。関数がインラインの場合、コンパイラーは、コンパイル時に関数が呼び出される各ポイントにその関数のコードのコピーを配置します。
インライン関数を変更すると、関数のすべてのクライアントを再コンパイルする必要が生じる可能性があります。コンパイラがすべてのコードをもう一度置き換える必要があるためです。そうしないと、古い機能が継続します。
関数をインライン化するには、関数名の前にキーワードをインラインで配置し、関数が呼び出される前に関数を定義します。定義された関数が行を超える場合、コンパイラーはインライン修飾子を無視できます。
クラス定義内の関数定義は、インライン指定子を使用しなくても、インライン関数定義です。
以下は、インライン関数を使用して最大2つの数値を返す例です。
#include <iostream>
using namespace std;
inline int Max(int x, int y) { return (x > y)? x : y; }
// Main function for the program
int main() {
cout << "Max (100,1010): " << Max(100,1010) << endl;
return 0;
}