web-dev-qa-db-ja.com

ヘッダーにC ++インライン関数があるのはなぜですか?

NB これは、インライン関数の使用方法やその機能についての質問ではなく、より多くの理由でインライン関数が実行される理由です。

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

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

それでは、なぜクラスのインライン実装は、ヘッダーファイルに含まれるhaveなのでしょうか?インライン関数に.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
106
thecoshman

inline関数の定義はヘッダーファイルにある必要はありませんが、インライン関数の1つの定義ルールのため、すべての翻訳に関数の同一の定義が存在する必要がありますそれを使用するユニット。

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

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

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

107
CB Bailey

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

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

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

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

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

inlineキーワードは、関数定義を複数の翻訳単位で表示できるようにすることで、コンパイラがこの最適化を適用することをeasierにしますが、キーワードを使用してもコンパイラを意味しません- hasは関数をインライン化し、notはキーワードを使用してもコンパイラが関数をインライン化することを禁止しません。

101
jalf

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

20
xanatos

その理由は、コンパイラが実際にdefinitionを見て、コール。

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

9
sbi

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

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

8
Erik

これは古いスレッドであることは知っていますが、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());
        }
    }
}
3
flamewave000

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

#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.
1

インライン関数

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

  • 重要:クラス内で関数を定義すると、自動的に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関数の両方がインラインになります

0
Saurabh Raoot