143

: これは、インライン関数の使用方法や機能に関する問題ではなく、なぜこのように実行されるのかという問題です。

クラスメンバー関数の宣言では、関数を as として定義する必要はありませんinline。関数の実際の実装のみです。たとえば、ヘッダー ファイルでは次のようになります。

struct foo{
    void bar(); // no need to define this as inline
}

では、なぜクラス関数のインライン実装をヘッダー ファイルに含める必要があるのでしょうかインライン関数を.cppファイルに配置できないのはなぜですか? インライン定義を.cppファイルに入れようとすると、次の行に沿ってエラーが発生します。

error LNK2019: unresolved external symbol 
"public: void __thiscall foo::bar(void)"
(?bar@foo@@QAEXXZ) referenced in function _main 
1>C:\Users\Me\Documents\Visual Studio 2012\Projects\inline\Debug\inline.exe 
: fatal error LNK1120: 1 unresolved externals
4

8 に答える 8

144

関数の定義はinlineヘッダー ファイルにある必要はありませんが、インライン関数には1 つの定義規則 ( ODR )があるため、関数を使用するすべての翻訳単位に関数の同一の定義が存在する必要があります。

これを実現する最も簡単な方法は、定義をヘッダー ファイルに入れることです。

関数の定義を単一のソース ファイルに入れたい場合は、それを宣言しないでくださいinline。宣言されていない関数はinline、コンパイラが関数をインライン化できないという意味ではありません。

関数を宣言する必要があるかどうかは、通常、 1 つの定義ルールのどのバージョンに従うのが最も理にかなっているかinlineに基づいて選択する必要があります。追加してから、後続の制約によって制限されることはほとんど意味がありません。inline

于 2011-02-20T12:35:14.697 に答える
131

それを見るには2つの方法があります:

  1. 関数呼び出しをインライン化するには、コンパイラが関数本体を認識できる必要があるため、インライン関数はヘッダーで定義されます。素朴なコンパイラがそれを行うには、関数本体が呼び出しと同じ翻訳単位にある必要があります。(最新のコンパイラは翻訳単位全体で最適化できるため、関数定義が別の翻訳単位にある場合でも関数呼び出しがインライン化される場合がありますが、これらの最適化は高価であり、常に有効になっているわけではなく、常にサポートされているとは限りません。コンパイラ)

  2. ヘッダーで定義された関数はマークする必要がありますinline。そうしないと、ヘッダーを含むすべての翻訳単位に関数の定義が含まれ、リンカーは複数の定義について文句を言うことになります (1 つの定義規則の違反)。キーワードはこれinlineを抑制し、複数の翻訳単位に (同一の) 定義を含めることができるようにします。

inlineこの 2 つの説明は、キーワードが期待どおりに機能しないという事実に要約されます。

C++ コンパイラは、プログラムの観察可能な動作を変更しない限り、インライン最適化(関数呼び出しを呼び出された関数の本体に置き換えて呼び出しオーバーヘッドを節約する) をいつでも自由に適用できます。

inlineキーワードは、関数定義を複数の翻訳単位で表示できるようにすることで、コンパイラがこの最適化を簡単に適用できるようにしますが、キーワードを使用しても、コンパイラ関数をインライン化する必要があるわけではなく、キーワードを使用しないことは意味しません。コンパイラが関数をインライン化するのを禁止します。

于 2011-02-20T13:01:15.210 に答える
29

これは C++ コンパイラの制限です。関数をヘッダーに配置すると、インライン化できるすべての cpp ファイルで関数の「ソース」を確認でき、インライン化をコンパイラで実行できます。それ以外の場合は、リンカーによってインライン化を行う必要があります (各 cpp ファイルは obj ファイルに個別にコンパイルされます)。問題は、リンカーでそれを行うのがはるかに難しいことです。「テンプレート」クラス/関数にも同様の問題があります。リンカはそれらのインスタンス化 (特殊化されたバージョンの作成) に問題があるため、コンパイラによってインスタンス化する必要があります。一部の新しいコンパイラ/リンカーは、コンパイラが最初のパスを実行する「2パス」のコンパイル/リンクを実行できます。次に、リンカーはその作業を実行し、コンパイラを呼び出して未解決のものを解決します(インライン/テンプレート...)

于 2011-02-20T12:30:49.477 に答える
13

c++inlineキーワードは誤解を招きます。「この関数をインライン化する」という意味ではありません。関数がインラインとして定義されている場合、それは単純に、すべての定義が等しい限り複数回定義できることを意味します。inline呼び出された時点でコードをインライン化する代わりに、実際の関数としてマークされた関数が呼び出されることは完全に合法です。

たとえば、テンプレート化されたクラスは実際にはクラスではなく、複数のバリエーションを作成できるクラスのテンプレートであるため、テンプレートにはヘッダー ファイルで関数を定義する必要があります。たとえば、Foo テンプレートを使用して Foo クラスを作成するときにコンパイラがFoo<int>::bar()関数を作成できるようにするには、 の実際の定義が表示されている必要があります。Foo<T>::bar()

于 2011-02-20T12:35:41.443 に答える
11

その理由は、呼び出しの代わりに定義をドロップできるようにするために、コンパイラが実際に定義を確認する必要があるためです。

C と C++ は非常に単純化されたコンパイル モデルを使用しており、コンパイラは一度に 1 つの翻訳単位しか認識しないことに注意してください。(これはエクスポートでは失敗します。これが、1 つのベンダーのみが実際に実装した主な理由です。)

于 2011-02-20T12:35:17.347 に答える
4

externこれが古いスレッドであることは知っていますが、キーワードについて言及する必要があると思いました。私は最近この問題に遭遇し、次のように解決しました

Helper.h

namespace DX
{
    extern inline void ThrowIfFailed(HRESULT hr);
}

Helper.cpp

namespace DX
{
    inline void ThrowIfFailed(HRESULT hr)
    {
        if (FAILED(hr))
        {
            std::stringstream ss;
            ss << "#" << hr;
            throw std::exception(ss.str().c_str());
        }
    }
}
于 2014-05-02T15:46:45.043 に答える
3

コンパイラは、それらをインライン化するためにそれらを見る必要があるためです。また、ヘッダー ファイルは、他の翻訳単位に一般的に含まれる「コンポーネント」です。

#include "file.h"
// Ok, now me (the compiler) can see the definition of that inline function. 
// So I'm able to replace calls for the actual implementation.
于 2011-02-20T12:29:25.297 に答える
0

インライン関数

C++ では、マクロはインライン関数に他なりません。SO 現在、マクロはコンパイラの制御下にあります。

  • 重要: クラス内で関数を定義すると、自動的にInlineになります

Inline 関数のコードは、それが呼び出された場所で置き換えられるため、関数呼び出しのオーバーヘッドが削減されます。

場合によっては関数のインライン展開がうまくいかない. など

  • インライン関数内で静的変数が使用されている場合。

  • 機能が複雑な場合。

  • 関数の再帰呼び出しの場合

  • 関数のアドレスが暗黙的または明示的に取得された場合

以下のようにクラス外で定義された関数はインラインになる可能性があります

inline int AddTwoVar(int x,int y); //This may not become inline 

inline int AddTwoVar(int x,int y) { return x + y; } // This becomes inline

クラス内で定義された関数もインラインになる

// Inline SpeedMeter functions
class SpeedMeter
{
    int speed;
    public:
    int getSpeed() const { return speed; }
    void setSpeed(int varSpeed) { speed = varSpeed; }
};
int main()
{
    SpeedMeter objSM;
    objSM.setSpeed(80);
    int speedValue = A.getSpeed();
} 

ここで getSpeed と setSpeed 関数の両方がインラインになります

于 2018-08-10T12:13:23.093 に答える