web-dev-qa-db-ja.com

インラインの使用はまだありますか?

inlineは、 here を読んだため廃止されたと信じていました。

関数をinlineとして指定する方法に関係なく、コンパイラーは無視することが許可されています。コンパイラーは、inlineとして指定された関数を呼び出す場所の一部、すべて、またはまったくインライン展開しない場合があります。

ただし、 Angew は、私が理解していないことを理解しているようです。 この質問 では、inlineがまだ有用かどうかについて、彼と私はかなりやり取りします。

この質問はではなくに関する質問です。

コンパイラは意のままにinlineできるので、そこでinlineは役に立たないことに注意してください:どこでinlineを使用して強制することができますか、示唆されません、コンパイルされたコードの変更?

82
Jonathan Mee

できる限り最善の方法で「秘密の理解」を説明しようとします。

ここには、2つのまったく異なる概念があります。 1つは、呼び出しサイトで関数本体を直接繰り返すことにより、関数呼び出しを置き換えるコンパイラーの機能です。もう1つは、複数の変換単位(=複数の.cppファイル)で関数を定義する可能性です。

最初のものは関数のインライン化と呼ばれます。 2番目はinlineキーワードの目的です。 歴史的にinlineキーワードは、inlineとマークされた関数をインライン化する必要があるというコンパイラーへの強い提案でもありました。コンパイラの最適化が向上するにつれて、この機能は廃止され、関数をインライン化するための提案としてinlineを使用することは実際に廃止されました。コンパイラは、それをより適切な最適化であると判断した場合、喜んで無視し、他の何かを完全にインライン化します。

明示的なinline– inlining関係を扱ったことを願っています。現在のコードには何もありません。

inlineキーワードの実際の目的は何ですか?簡単です:inlineとマークされた関数は、One Definition Rule(ODR)に違反することなく、複数の翻訳単位で定義できます。次の2つのファイルを想像してください。

file1.cpp

int f() { return 42; }

int main()
{ return f(); }

file2.cpp

int f() { return 42; }

このコマンド:

> gcc file1.cpp file2.cpp

シンボルfが2回定義されていると文句を言って、リンカーエラーを生成します。

ただし、関数をinlineキーワードでマークすると、コンパイラとリンカーに具体的に次のように伝えます。「この関数の複数の同一の定義がnotを実行するとエラーが発生することを確認します!」

したがって、次のように機能します。

file1.cpp

inline int f() { return 42; }

int main()
{ return f(); }

file2.cpp

inline int f() { return 42; }

これらの2つのファイルを一緒にコンパイルおよびリンクしても、リンカーエラーは発生しません。

もちろん、fの定義は、必ずしもファイル内にある必要はありません。代わりに#includedヘッダーファイルから取得できます。

f.hpp

inline int f() { return 42; }

file1.cpp

#include "f.hpp"

int main()
{ return f(); }

file2.cpp

#include "f.hpp"

基本的に、関数定義をヘッダーファイルに書き込むには、inlineとしてマークする必要があります。そうしないと、複数の定義エラーが発生します。


パズルの最後のピースは次のとおりです。インライン化とは何の関係もないのに、キーワードが実際にinlineと綴られるのはなぜですか?理由は簡単です。関数をインライン化する(つまり、呼び出しサイトで本体を繰り返して呼び出しを置き換える)ために、コンパイラは最初にhaveである必要があります。

C++は、compilerが現在生成しているもの以外のオブジェクトファイルにアクセスできない、別個のコンパイルモデルに従います。したがって、関数をインライン化するには、その定義が現在の翻訳単位の一部である必要があります。複数の翻訳単位でインライン化できるようにしたい場合は、すべての翻訳単位で定義する必要があります。通常、これは多重定義エラーにつながります。したがって、関数をヘッダーに配置し、その定義をどこでも#includeどこでもインライン化できるようにする場合、複数の定義エラーを防ぐためにinlineとしてマークする必要があります。

今日でも、コンパイラーはすべての関数が適切であるとインライン化されますが、その関数の定義にアクセスできる必要があります。したがって、「これをインライン化してください」というヒントとしてinlineキーワードは必要ありませんが、enableを選択した場合にインライン化を行うためにコンパイラを使用する必要があることがあります。そうする。これがないと、翻訳単位に定義を取得できない可能性があり、定義がないと、コンパイラは単に関数をインライン化できません。

コンパイラーはできません。リンカーはできます。最新の最適化手法には、リンク時コード生成(プログラム全体の最適化)が含まれます。この場合、最適化は、実際のリンクの前に、リンクプロセスの一部としてすべてのオブジェクトファイルに対して実行されます。このステップでは、すべての関数定義がもちろん利用可能であり、プログラム内のどこでも単一のinlineキーワードを使用しなくてもインライン化が完全に可能です。しかし、この最適化は、特に大規模なプロジェクトの場合、一般的にビルド時間のコストがかかります。これを念頭に置いて、インライン化をLTCGのみに依存することは最良の選択肢ではないかもしれません。


完全を期すために:最初の部分で少しごまかしました。 ODRプロパティは、実際にはinlineキーワードのプロパティではなく、インライン関数(言語の用語)のプロパティです。インライン関数のルールは次のとおりです。

  • リンカエラーを発生させずに複数の翻訳単位で定義できます
  • 使用されるすべての翻訳単位で定義する必要があります
  • その定義はすべて、トークン用トークンとエンティティ用エンティティが同一である必要があります

inlineキーワードは、関数をインライン関数に変換します。関数をインラインとしてマークする別の方法は、クラス定義で関数を直接宣言するだけでなく定義することです。このような関数は、inlineキーワードがなくても自動的にインライン化されます。

75
Angew

inlineは、あなたが述べた理由から、現在はほとんど外部リンケージ指定子にすぎません。

そう、それは確かに用途がありますが、実際に関数をインライン化するのとは異なります。複数の定義エラーを取得する代わりに、コンパイル単位間で同じメソッドを複数回定義し、それらを適切にリンクすることができます。

//header.h
inline void foo() {}
void goo() {}

//cpp1.cpp
#include "header.h"

//cpp2.cpp
#include "header.h"

// foo is okay, goo breaks the one definition rule (ODR)

実際に関数のインライン化を強制するのはコンパイラ次第で、特定のattributesまたはpragmasまたは(__forceinline)またはその他。

簡単に言えば、ODRを壊すことなくヘッダーで関数を定義できます...

45
Luchian Grigore