20

クラス定義自体の内部でメンバー関数を定義する場合、それは必然的にインラインで処理されますか、それとも無視できるコンパイラへの単なる要求ですか。

4

7 に答える 7

19

はい、クラス本体内で定義されている関数は暗黙的にinlineです。

(宣言された他の関数と同様inlineに、関数が呼び出される場所でコンパイラがインライン展開を実行する必要があるという意味ではありません。定義が必要であるという要件と組み合わせて、「1 つの定義規則」の許可された緩和を有効にするだけです。関数が使用されるすべての翻訳単位に含まれます。)

于 2010-03-23T16:38:12.097 に答える
5

他の人が述べたように、クラス内で定義されたメソッドは自動的にインラインで要求されます。理由を理解すると便利です。

そうではなかったとしましょう。そのような関数のコードを生成する必要があり、それが呼び出されるたびに、サブルーチン命令へのジャンプはリンカを介してその場所を参照する必要があります。

class A {
public:
  void f() { ... your code ... }
};

このコードが表示されるたびに、インラインでない場合、コンパイラはコードが生成される必要があると想定することしかできないため、シンボルを生成します。次のようだったとします。

A__f_v:

そのシンボルがグローバルである場合、このクラス コードを別のモジュールに複数回含めると、リンク時に複数定義されたシンボル エラーが発生します。したがって、グローバルにはなりません。代わりに、ファイルローカルです。

上記のヘッダー ファイルを多数のモジュールにインクルードするとします。それぞれで、そのコードのローカル コピーを生成します。これは、まったくコンパイルしないよりはましですが、本当に必要なコードが 1 つしかないときに、複数のコードのコピーを取得することになります。

これは、次の結論につながります。コンパイラが関数をインライン化しない場合は、インライン化を要求せずに、関数をどこかで一度宣言する方がはるかに優れています。

残念ながら、インラインであるものとそうでないものは移植性がありません。これは、コンパイラ ライターによって定義されます。経験則としては、オーバーヘッドを取り除くため、常にすべてのライナー、特に関数を呼び出すだけのすべての関数をインラインにすることです。3 行以下の線形コードは、ほぼ間違いなく問題ありません。しかし、コードにループがある場合、問題は、コンパイラがそれをインラインで許可するかどうか、さらに言えば、希望どおりに実行されたとしても、どれだけのメリットがあるかということです。

次のインライン コードを検討してください。

inline int add(int a, int b) { return a + b; }

ソース コード内のプロトタイプとほぼ同じくらい小さいだけでなく、インライン コードによって生成されるアセンブリ言語は、ルーチンの呼び出しよりも小さくなります。したがって、このコードはより小さく、より高速です。

そして、たまたま定数を渡している場合:

int c= add(5,4);

コンパイル時に解決され、コードはありません。

gcc では、コードをインライン化しなくても、それがファイルに対してローカルであれば、こっそりとインライン化することに最近気付きました。別のソースモジュールで関数を宣言した場合にのみ、呼び出しが最適化されません。

スペクトルの反対側で、1000 行のコードでインラインを要求するとします。たとえあなたのコンパイラがそれに対応するほどばかげているとしても、保存できるのは呼び出し自体だけであり、呼び出しのたびにコンパイラがすべてのコードを貼り付けなければならないというコストがかかります。そのコードを n 回呼び出すと、 、コードはルーチンのサイズ * n だけ大きくなります。そのため、10 行を超えるものはインライン化する価値がほとんどありません。ただし、呼び出される回数が非常に少ないという特殊なケースは除きます。その例として、他に 2 人だけが呼び出すプライベート メソッドがあります。

ループを含むメソッドをインライン化するように要求する場合、頻繁に実行される回数が少ない場合にのみ意味があります。しかし、100 万回反復するループを考えてみましょう。コードがインライン化されていても、呼び出しに費やされる時間の割合はごくわずかです。そのため、ループを含むメソッドがあり、とにかく大きくなる傾向がある場合、それらはヘッダー ファイルから削除する価値があります。なぜなら、それらは a) コンパイラによってインラインとして拒否される傾向があり、b) たとえインライン化されていたとしても、一般的に何の利益ももたらさない

于 2010-03-23T17:58:06.640 に答える
3

これは、インラインの要求としてコンパイラによって必然的に処理されますが、これは無視できます。ヘッダーでいくつかの関数を定義するためのいくつかのイディオム (空の仮想デストラクタなど) といくつかの必要なヘッダー定義 (テンプレート関数) がありますが、それ以外の詳細についてはGotW #33を参照してください。

コンパイラは、あなたが要求したことのない関数をインライン化することさえあると指摘する人もいますが、それが関数のインライン化を要求する目的に反するかどうかはわかりません。

于 2010-03-23T16:42:32.213 に答える
3

これは確かにインライン化されていますが、インライン リクエストはコンパイラによって無視される可能性があります。

于 2010-03-23T16:45:12.720 に答える
2

2003 ISOC++標準によると

7.1.2 / 2インライン指定子を使用した関数宣言(8.3.5、9.3、11.4)は、インライン関数を宣言します。インライン指定子は、呼び出しポイントでの関数本体のインライン置換が通常の関数呼び出し
メカニズムよりも優先されることを実装に示します。
呼び出しの時点でこのインライン置換を実行するために実装は必要ありません。ただし、このインライン
置換が省略されている場合でも、7.1.2で定義されているインライン関数の他の規則は尊重されます。

7.1.2 / 3クラス定義内で定義された関数は、インライン
関数です。インライン指定子は、ブロックスコープ関数宣言に表示されません。

7.1.2 / 4インライン関数は、
それが使用されるすべての変換単位で定義され、すべての
場合でまったく同じ定義を持つものとします(3.2)。[注:インライン関数の呼び出しは、
その定義が変換ユニットに表示される前に発生する可能性があります。]外部リンケージを持つ関数が1つの翻訳単位でインラインで宣言されている場合、それが表示されるすべての翻訳単位でインラインで宣言されるものとします。診断は必要ありません。外部リンケージを持つインライン関数は、すべての変換ユニットで同じアドレスを持つ必要があります。externインライン
関数の静的ローカル変数は、常に同じオブジェクトを参照します。
externインライン関数の文字列リテラルは、異なる変換で同じオブジェクトです
単位。

于 2010-03-23T16:51:34.827 に答える
2

これは、無視できるコンパイラへの要求です。

于 2010-03-23T16:36:50.047 に答える
1

まとめてはいけないことが2つあります。

  1. 関数をインラインとしてマークする方法:署名の前にインラインで定義するか、宣言ポイントで定義します。
  2. コンパイラがそのようなインラインマーキングを処理する内容:関数をインラインとしてどのようにマークしたかに関係なく、コンパイラは関数を要求として処理します。
于 2010-03-23T16:52:26.573 に答える