定義により、インラインメンバ関数はヘッダーに配置する必要があることを知っています。しかし、関数の実装をヘッダーに配置できない場合はどうでしょうか?この状況を見てみましょう。
ファイルA.h
#pragma once
#include "B.h"
class A{
B b;
};
ファイルB.h
#pragma once
class A; //forward declaration
class B{
inline A getA();
};
循環インクルードのため、getA
の実装を
B.cpp
#include "B.h"
#include "A.h"
inline A B::getA(){
return A();
}
コンパイラーはgetA
をインライン化しますか?もしそうなら、どのインラインキーワードが重要なものですか(ヘッダー内のキーワードまたは.cppファイル内のキーワード)?インラインメンバー関数の定義を.cppファイルに追加する別の方法はありますか?
C++ FAQ からの引用:
注:関数が単一の.cppファイルでのみ使用されている場合を除き、関数の定義({...}の間の部分)をヘッダーファイルに配置する必要があります。特に、インライン関数の定義を.cppファイルに入れて、他の.cppファイルから呼び出すと、リンカーから「未解決の外部」エラーが発生します。
コンパイラーは、インライン関数の使用を見つけるたびに、インライン関数のdefinitionを確認する必要があります。通常、インライン関数がヘッダーファイルに配置されている場合に可能です。
コンパイラはgetAをインライン化しますか?
いいえ、getA()
の使用がB.cpp自体にある場合を除きます。
もしそうなら、どのインラインキーワードが重要なものですか(ヘッダーの1つまたはcppの1つ)?
ベストプラクティス :クラス本体以外の定義のみ。
インラインメンバー関数の定義をcppファイルに追加する別の方法はありますか?
いいえ、少なくともわかりません。
B.cppの範囲外ではできません。コンパイラは、コンパイル単位ごとに動作します。つまり、各.cppファイルを個別にコンパイルします。したがって、C.cppをコンパイルする場合、getA()のコードは使用できず、関数呼び出しを実行する必要があります。リンカーに修正してもらいます(または、実際にWordを使用してインラインしようとした場合、リンカーエラーが発生します。inline
はstatic
と同じ品質です)。
唯一の例外はLTCG、つまり、リンク時コード生成であり、これは新しいコンパイラーで利用可能です。
この場合のアプローチの1つは、インラインコードを含む別のヘッダーファイル(* .inlファイルと呼ばれることもあります)を持つことです。
編集:どのインラインが関連しているのか-それはクラス定義、つまりヘッダーファイルのインラインです。多くのコンパイラーは、インライン化できるものとすべきものについて独自の考えを持っていることに留意してください。たとえば、gccはインライン化を完全に無効にする(-O0)か、インライン化する価値があるとみなすもの(-O3など)をインライン化できます。
私は反対の方向からこれについて行きます。
関数にインライン宣言を追加しないでください(必要な場合を除く)。
関数/メソッドにインライン宣言を追加する必要があるのは、クラス宣言以外のヘッダーファイルで関数を定義する場合のみです。
class X
{
public:
int getX() { return 4;} // No inline because it is part of the class.
// The compiler knows that needs an inline tag
int getY();
int getZ();
};
inline
int X::getY() { return 5;} // This needs to be explicitly declared inline.
// Otherwise the linker will complain about
// multiple definitions in compilation units.
// Never declare anything inline in the cpp file.
int X::getZ() { return 6; }
より具体的な場合。
すべてのインライン仕様を削除します。彼らはあなたが彼らがしていると思うことをしていません。
最近では、ほとんどのコンパイラーは、コンパイル時だけでなく、リンク時にインライン化を実行できます。関数がインライン化の恩恵を受ける可能性が高い場合は、Link Timeオプティマイザーがそれを行う可能性があります。
リンカがそれに到達するまでに、コンパイラ出力のインラインステータスについてはほとんど利用できません。ただし、たとえば、インライン関数またはクラステンプレートインスタンスが複数のコンパイルユニットに表示されるため、コンパイラは特定のオブジェクトを収集可能としてフラグを立てます。メイン関数が2回定義されているなど、複数のシンボルが名前を共有している場合、エラーが発生します。これは、生成される実際のコードに影響を与えません。
ISO/IEC 14882:2014
すべてのプログラムには、そのプログラムでodrで使用されるすべての非インライン関数または変数の定義が1つだけ含まれます。診断は不要です。定義はプログラムに明示的に現れるか、標準ライブラリまたはユーザー定義ライブラリで見つかるか、(適切な場合)暗黙的に定義されます(12.1、12.4および12.8を参照)。インライン関数は、odrが使用されるすべての翻訳単位で定義されます。
これが私がやった方法です。
ファイルA.h
#pragma once
#include "B.h"
class A {
B b;
};
ファイルB.h
#pragma once
class B {
public:
template<class T> inline T getA() {
assert(NULL); // Use 'getA<A>()' template specialization only!
return NULL;
}
};
class A; // Forward declaration
template<> inline A B::getA<A>();
ファイルC.h
#pragma once
#include "A.h"
#include "B.h"
// Implement template specialization here!
template<> inline A B::getA<A>() { return A(); }
「C.h」ファイルをインクルードして、getA()メソッドを使用できるようにします。元のコードでの唯一の変更点は、getA()メソッドをプライベートではなくパブリックとして定義する必要があることです。
ただし、多くの人が説明したように、これは実際には役に立ちません。