関数/メソッドのキーワード「インライン」はいつ書くべきですか?


562

inlineC ++の関数/メソッドのキーワードはいつ書くべきですか?

いくつかの回答、いくつかの関連する質問を確認した後:

  • C ++で関数/メソッドのキーワード「インライン」を記述すべきでないのはいつですか?

  • 関数/メソッドを「インライン」にするタイミングをコンパイラが知らないのはいつですか?

  • 関数/メソッドの「インライン」を書き込むときにアプリケーションがマルチスレッド化されているかどうかは重要ですか?


40
ヘッダーで関数を定義する場合は、インラインで宣言する必要があります。そうしないと、関数の複数の定義に関するリンカーエラーが発生します。
マーティンヨーク

15
@Martin:クラス定義でない限り、うるさい。
David Thornley、

20
@David:余分にうるさいのは、そのような関数が暗黙的にマークされているためですinline(9.3 / 2)。
2014


C ++ FAQのインライン関数も参照してください。彼らはインラインの非常に良い扱いをしています。
jww

回答:


882

ああ、私のペットのおしっこの一つ。

inline以上のようなものですstaticか、externあなたの関数をインライン化するコンパイラを伝えるディレクティブより。 externstaticinlineリンカではなく、コンパイラによってほぼ独占的に使用するリンケージ・ディレクティブは、あります。

inline関数をインライン化する必要があるとコンパイラに示唆していると言われています。それは1998年には本当だったかもしれませんが、10年後、コンパイラーはそのようなヒントを必要としません。コードの最適化に関しては、人間は通常間違っていることは言うまでもありません。そのため、ほとんどのコンパイラは「ヒント」を無視します。

  • static-変数/関数名は他の翻訳単位では使用できません。リンカーは、静的に定義された変数/関数を別の翻訳単位から誤って使用しないようにする必要があります。

  • extern-この翻訳単位でこの変数/関数名を使用しますが、定義されていなくても文句を言わないでください。リンカはそれを整理し、いくつかの外部シンボルを使用しようとしたすべてのコードがそのアドレスを持っていることを確認します。

  • inline-この関数は複数の翻訳単位で定義されますので、心配しないでください。リンカは、すべての変換ユニットが変数/関数の単一のインスタンスを使用することを確認する必要があります。

注:テンプレートにinlineinlineすでにリンケージセマンティクスがあるため、通常、テンプレートの宣言は無意味です。ただし、テンプレートの明示的な特殊化とインスタンス化を使用する必要inlineがあります。


質問に対する具体的な回答:

  • C ++で関数/メソッドのキーワード「インライン」を書く必要があるのはいつですか?

    ヘッダーで関数を定義する場合のみ。より正確には、関数の定義が複数の翻訳単位で表示される場合のみです。コードを最適化する際にコンパイラーが処理するためのより多くの情報を提供するので、ヘッダーファイルで小さな(1つのライナーのような)関数を定義することをお勧めします。また、コンパイル時間が長くなります。

  • C ++で関数/メソッドのキーワード「インライン」を記述すべきでないのはいつですか?

    コンパイラーがインライン化するとコードがより高速に実行されると思うからといって、インラインを追加しないでください。

  • 関数/メソッドを「インライン」にするタイミングをコンパイラが知らないのはいつですか?

    一般に、コンパイラーはあなたよりもこれをうまく行うことができます。ただし、関数定義がない場合、コンパイラーにはコードをインライン化するオプションがありません。最大限に最適化されたコードでは、通常、private要求するかどうかに関係なく、すべてのメソッドがインライン化されます。

    GCCではインライン化を回避するための余地として、を使用し__attribute__(( noinline ))、Visual Studioではを使用します__declspec(noinline)

  • 関数/メソッドの「インライン」を書き込むときにアプリケーションがマルチスレッド化されているかどうかは重要ですか?

    マルチスレッド化は、インライン化にはまったく影響しません。


172
+1 ...で見たインラインの最良の説明(永遠に)。私はあなたをだまして、これをインラインキーワードのすべての私の説明で使用します。
マーティンヨーク

6
@Ziggy、私が言おうとしていたのは、コンパイラーのインライン化とinlineキーワードが関連していないということでした。しかし、あなたは正しい考えを持っています。原則として、インライン化によって何が改善されるかを推測すると、エラーが発生しやすくなります。その規則の例外はワンライナーです。
deft_code

4
この答えは私を少し混乱させます。あなたは、コンパイラーが物事をよりインライン化できる/インライン化できないということすべてを言っています。次に、ヘッダーに1つのライナー/小さな関数を配置する必要があり、コンパイラーは関数定義なしにコードをインライン化できないと言います。これらは少し矛盾していませんか?なぜすべてをcppファイルに入れて、コンパイラに決定させないのですか?
user673679

5
コンパイラーは、呼び出しサイトで定義が使用可能な関数呼び出しのみをインライン化します。すべての関数をcppファイルに残すと、インライン化がそのファイルに制限されます。コンパイル速度へのコストはごくわずかであり、コンパイラーが呼び出しをインライン化することがほぼ保証されているため、.hで小さな1ライナーをインラインで定義することをお勧めします。コンパイラーのインライン化についての私のポイントは、コンパイラーがあなたよりもはるかに優れている、最適化の黒人芸術のポートであるということです。
deft_code

8
インターネットの累積的な知識を説明するために何かを読むときはいつでも、ジョンロートンの有名な引用を考えなければなりません
IInspectable 2013

60

このスレッドのすばらしい答えすべてに貢献し、残りの誤解を分散させる説得力のある例を示したいと思います。

次のような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
    

    議論

    1. インライン関数の定義が同じであっても、C ++コンパイラはそうでない場合はフラグを立てません(実際には、個別のコンパイルのため、チェックする方法がありません)。これを確実にするのはあなた自身の義務です!

    2. として宣言されているように、リンカーは1つの定義ルールについて文句を言いません。しかし、inline111.cppは最初の翻訳単位(実際に呼び出しているコンパイラによって処理)、コンパイラはインスタンス化し、その時に最初でコール出会いinline111.cpp。コンパイラがプログラムの他の場所(たとえばinline222.cppから)からの呼び出しを拡張しないと決定した場合、への呼び出しは常にinline111.cppから生成されたインスタンス(inline222.cpp内への呼び出し)にリンクされます。fun()inlinefun()fun()fun()fun()fun()その翻訳単位でインスタンスが生成されることもありますが、リンクは解除されたままになります)。確かに、それは同じ&fun = 0x4029a0プリントアウトから明らかです。

    3. 最後に、実際に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
    

    議論

    1. このケースは、ケースAで議論されたことを主張します。

    2. あなたが実際の呼び出しをコメントアウトした場合という、重要な点に注意してください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
    

    議論

    1. などがされ、ここで説明し-O2最適化をするために、コンパイラを奨励し、実際に展開する(つまりもお知らせインライン化することが可能な機能を-fno-inlineあるデフォルトの最適化オプションなし)。ここでのアウトプリントから明らかなように、fun()は実際にはインライン展開されており(その特定の翻訳単位での定義に従って)、2つの異なる fun()プリントアウトになります。これにもかかわらず、同一のプリントアウトから明らかなように、(規格で要求されているように)グローバルにリンクされたインスタンスはまだ1つしかありません。fun() &fun

8
あなたの答えは、言語がそのようなinline関数を未定義の動作にする理由を説明する投稿です。
R Sahu

コンパイルとリンクが別々で、それぞれ.cppが独自の翻訳単位であるケースも追加する必要があります。できれば、-flto有効/無効のケースを追加してください。
syockit

C ++リファレンスでは、「外部リンケージのあるインライン関数または変数(C ++ 17以降)が異なる変換単位で異なる方法で定義されている場合、動作は未定義です」と明示的に述べています。したがって、あなたが書いたものはコンパイルとリンケージのプロセスのオーケストレーションの副作用であるため、GCC固有のものです。また、これはバージョン間で異なる場合があることに注意してください。
Petr Fiedler

27

テンプレートの特殊化を行うときは、関数を明示的にインライン化する必要があります(特殊化が.hファイルにある場合)。


21

1)今日では、ほとんどありません。関数をインライン化することをお勧めしますが、コンパイラはあなたの助けなしにそれを行います。

2)常に。#1を参照してください。

(あなたがあなたの質問を2つの質問に分けたことを反映するように編集されました...)


はい。インラインはコンパイラへのヒントにすぎず、無視してかまいません。最近、コンパイラーはおそらくインライン化に最適な関数をプログラマーよりよく知っています。
マーク・バイアーズ

1
はい、しかしそれはそれほど重要ではありません-関数がインライン化されるためには、その本体は同じコンパイル単位(例えば、ヘッダー)になければなりません。これはCプログラムではあまり一般的ではありません。
マイケルコーン

1
非メンバー関数テンプレート(別名非静的関数テンプレート)の定義には、インラインは必要ありません。1つの定義ルール(3.2 / 5)を参照してください。
deft_code 2009年

2
-1:inlineは、たとえば、ヘッダーファイルで関数を定義するために必要です(そのような関数をいくつかのコンパイルユニットでインライン化するために必要です)。
Melebius 2014年

1
@Étienneは実装固有です。標準ごとに、1つの定義ルールがあります。つまり、関数定義を単純に複数の翻訳単位に含めると、エラーが発生します。ただし、その関数にinline指定子がある場合、そのインスタンスはリンカーによって自動的に1つに折りたたまれ、ODRは使用されません。
ルスラン

12

C ++で関数/メソッドのキーワード「インライン」を記述すべきでないのはいつですか?

関数がヘッダーで宣言され、.cppファイルで定義されている場合は、キーワードを記述しないでください。

コンパイラーは、関数/メソッドを「インライン」にするタイミングをいつ知っているのですか?

そのような状況はありません。コンパイラーは関数をインライン化できません。関数の一部またはすべての呼び出しをインライン化するだけで実行できます。関数のコードを取得していない場合は実行できません(その場合、リンカーは、実行できる場合は実行する必要があります)。

関数/メソッドの「インライン」を書き込むときにアプリケーションがマルチスレッド化されているかどうかは重要ですか?

いいえ、それはまったく問題ではありません。


.cppファイルでインラインを使用することが適切な場合があります。たとえば、完全に実装固有のコードに最適化を適用します。
ロビンデイビス

@RobinDaviesが回答を更新しました。私が書きたかったことをあなたは誤解しているようです。
Johannes Schaub-litb

5
  • コンパイラーは、関数/メソッドを「インライン」にするタイミングをいつ知っているのですか?

これは、使用するコンパイラによって異なります。今日のコンパイラーはインライン化の方法を人間よりよく知っていることを盲目的に信じないでください。最適化のヒントではなくリンケージディレクティブであるため、パフォーマンス上の理由から使用しないでください。これらの議論はイデオロギー的に正しいことに同意しますが、現実に正しく遭遇することは別のことかもしれません。

複数のスレッドを読んだ後、好奇心から私が作業しているコードへのインラインの影響を試しましたが、結果として、GCCでは測定可能なスピードアップが得られ、Intelコンパイラではスピードアップされませんでした。

(詳細:クラス外で定義された重要な関数がほとんどない数学シミュレーション、GCC 4.6.3(g ++ -O3)、ICC 13.1.0(icpc -O3);重要なポイントにインラインを追加すると、GCCコードで+ 6%スピードアップしました)。

したがって、GCC 4.6を最新のコンパイラとして認定した場合、CPU集中型のタスクを記述してボトルネックがどこにあるかを正確に把握している場合、インラインディレクティブは依然として重要です。


6
あなたの主張を裏付ける証拠がもっと欲しいです。テストするコードと、インラインキーワードあり/なしのアセンブラ出力を提供してください。いくつものことでパフォーマンスが向上する可能性があります。
void.pointer 2014年

1
最後に、他の人が言うことを繰り返すだけでなく、実際にそれらのステートメントを検証する人。Gccは確かに、インラインキーワードをヒントと見なしています(clangでは完全に無視されていると思います)。
MikeMB 2016

@ void.pointer:なぜこれが信じられないほど難しいのですか?オプティマイザがすでに完璧だった場合、新しいバージョンではプログラムのパフォーマンスを向上させることができませんでした。しかし、彼らは定期的にそうしています。
MikeMB 2016

3

実際には、ほとんどありません。あなたがしていることは、コンパイラが特定の関数をインラインにすることを提案することです(たとえば、この関数へのすべての呼び出し/ w本体を置き換える)。もちろん、保証はありません。コンパイラはディレクティブを無視する場合があります。

コンパイラーは、通常、このようなことを検出して最適化します。


7
問題は、それがあるinline持っているセマンティック C ++(例えば複数の定義が扱われる方法で)いくつかのケースでは重要であり、(例えばテンプレート)の違いを。
Pavel Minaev 2009年

4
インラインは、シンボルに複数の定義がある場合を解決するために使用されます。ただし、テンプレートはすでに言語によって処理されています。1つの例外は、特別なテンプレート関数で、テンプレートパラメータはもうありません(template <>)。これらはテンプレートよりも関数のように扱われるため、リンクするにはinlineキーワードが必要です。
deft_code 2009年

2

最適化を有効にせずにコンパイルすると、デフォルトではgccは関数をインライン化しません。ビジュアルスタジオについて知らない– deft_code

/ FAcsを使用してコンパイルし、アセンブリコードを確認することで、Visual Studio 9(15.00.30729.01)についてこれを確認しました。コンパイラーは、デバッグモードで最適化を有効にせずにメンバー関数の呼び出しを生成しました。関数が__forceinlineでマークされていても、インラインランタイムコードは生成されません。


1
/ Wallがインラインとしてマークされていても実際にはインライン化されなかった関数について
通知される

0

戻り値の型の前に、最初に入れたいと思います。しかし、ほとんどのコンパイラはそれを無視します。それが定義されていて、コードのブロックが小さい場合、ほとんどのコンパイラはとにかくインラインであると見なします。


0

ライブラリを作成している場合や特別な理由がない限り、代わりにリンク時の最適化を忘れてinline使用することができます。これは、コンパイル単位全体でインライン化するために関数定義がヘッダーに含まれている必要があるという要件を取り除きます。これは、まさに許可されていることです。inline

(ただし、リンク時最適化を使用しない理由はありますか?


0

インラインキーワードは、関数呼び出しを関数の本文で置き換えるようにコンパイラーに要求し、最初に式を評価してから渡します。戻りアドレスを格納する必要がなく、関数にスタックメモリが必要ないため、関数呼び出しのオーバーヘッドが削減されます。引数。

いつ使用するか:

  • パフォーマンスを改善するには
  • 通話のオーバーヘッドを減らすため。
  • コンパイラへのリクエストにすぎないため、特定の関数はインライン化されません*大きな関数
    • 条件付き引数が多すぎる関数
    • 再帰的なコードとループなどのコード

これが実際には当てはまらないことを知っておくと役に立ちます。最適化レベル-O0から-Ofastは、関数がインライン化されるかどうかを決定するものです。通常のコンパイルでのインライン化(​​-O0)はinline、CおよびC ++で使用するかどうかに関係なく、関数をインライン化しません。Cインライン:stackoverflow.com/a/62287072/7194773 C ++インライン: stackoverflow.com/a/62230963/7194773
Lewis Kelsey

0

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 inlineatファイルスコープはコンパイラにのみ影響します。これは、コンパイラにとって意味があります。変換ユニットで使用されている場合にのみ、この関数/データのシンボルを発行し、通常の静的シンボルとして発行します(.globlディレクティブなしでin.text /.dataを格納)。アセンブラにとって、との間に違いはstaticありませんstatic inline

extern inlineこの宣言は、このシンボルを翻訳単位で定義するか、コンパイラエラーをスローする必要があることを意味します。それが定義されていた場合、その後の正規として扱いinline、アセンブラ、リンカにそこの間に違いないであろうextern inlineinline、そう、これが唯一のコンパイラのガードです。

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名前空間では、これこれを参照してください


-1

コードを開発およびデバッグするときは、省略inlineしてください。デバッグが複雑になります。

それらを追加する主な理由は、生成されたコードを最適化するためです。通常、これはコードスペースの増加と速度をトレードオフしますがinline、コードスペースと実行時間の両方を節約することもあります。

アルゴリズムの完了前にこのようなパフォーマンスの最適化について考えることは、時期尚早の最適化です。


12
inline関数は通常、最適化でコンパイルしない限りインライン化されないため、デバッグに影響を与えることはありません。これはヒントであり、要求ではないことを忘れないでください。
Pavel Minaev 2009年

3
最適化を有効にせずにコンパイルすると、デフォルトではgccは関数をインライン化しません。ビジュアルスタジオについて知らない
deft_code '18

デバッグを有効にした巨大なg ++プロジェクトに取り組みました。たぶん他のオプションがそれを妨げたが、inline機能はインライン化された。それらに意味のあるブレークポイントを設定することは不可能でした。
ウォリック2009年

2
デバッグを有効にしても、gccのインライン化は停止しません。最適化が有効な場合(-O1以上)、gccは最も明白なケースをインライン化しようとします。従来、GDBはブレークポイントとコンストラクター、特にインラインコンストラクターで苦労していました。しかし、それは最近のバージョンで修正されています(少なくとも6.7、おそらくもっと早い)。
deft_code 2009年

2
追加inlineしても、それ自体でインライン化するかどうかを判断できる最新のコンパイラーでコードを改善することはできません。
David Thornley、

-1

インライン化する必要がある場合:

1.パラメータの受け渡し、コントロールの転送、コントロールの戻りなどのように、関数が呼び出されたときに発生するオーバーヘッドを回避したい場合。

2.関数は小さく、頻繁に呼び出される必要があり、80-20ルールに従って、プログラムのパフォーマンスに大きな影響を与える関数をインラインで作成しようとするため、インラインで作成することは非常に有利です。

私たちが知っているように、インラインはレジスターに似たコンパイラーへの要求にすぎず、オブジェクトコードサイズでコストがかかります。


「インラインは、レジスターに似たコンパイラーへの単なる要求です」どちらも要求ではなく、最適化にも関係がないため、似ています。inline最適化のヒントとしてのステータスを失いました。ほとんどのコンパイラは、それを複数の定義を許可するためにのみ使用します-IMOのように。さらに、C ++ 11以降register、「コンパイラーよりも最適化の方法をよく知っている」という以前の意味が完全に廃止されています。これは、現在の意味を持たない予約語にすぎません。
underscore_d

@underscore_d:Gccはまだinlineある程度耳を傾けています。
MikeMB 2016

-1

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;
}

詳細については、こちらを参照してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.