3 つの異なる型パラメーターでインスタンス化される C++ テンプレート クラスがあります。これらの型の 1 つだけのためにクラスが持つ必要があり、他の 2 つの型では決して呼び出されないメソッドがあります。
そのメソッドのオブジェクト コードは 3 回 (テンプレートがインスタンス化されるすべての型に対して) 生成されますか、それともオブジェクト コードは (実際に使用される型に対して) 1 回だけ生成されますか?
仮想メンバー関数はクラス テンプレートがインスタンス化されるときにインスタンス化されますが、非仮想メンバー関数は呼び出された場合にのみインスタンス化されます。
これは、C++ 標準の [temp.inst] でカバーされています (C++11 では、これは §14.7.1/10 です。C++14 では、これは §14.7.1/11 であり、C++17 では§17.7.1/9 です。以下 C++17 からの抜粋)
実装は、関数テンプレート、変数テンプレート、メンバー テンプレート、非仮想メンバー関数、メンバー クラス、クラス テンプレートの静的データ メンバー、または
constexpr
if ステートメントのサブステートメントを暗黙的にインスタンス化してはなりません (9.4.1) 。 、そのようなインスタンス化が必要でない限り
また、一部のメンバー関数が特定のテンプレート パラメーターに対してインスタンス化できない場合でも、クラス テンプレートをインスタンス化できることに注意してください。例えば:
template <class T>
class Xyzzy
{
public:
void CallFoo() { t.foo(); } // Invoke T::foo()
void CallBar() { t.bar(); } // Invoke T::bar()
private:
T t;
};
class FooBar
{
public:
void foo() { ... }
void bar() { ... }
};
class BarOnly
{
public:
void bar() { ... }
};
int main(int argc, const char** argv)
{
Xyzzy<FooBar> foobar; // Xyzzy<FooBar> is instantiated
Xyzzy<BarOnly> baronly; // Xyzzy<BarOnly> is instantiated
foobar.CallFoo(); // Calls FooBar::foo()
foobar.CallBar(); // Calls FooBar::bar()
baronly.CallBar(); // Calls BarOnly::bar()
return 0;
}
Xyzzy::CallFoo() は BarOnly::foo() のようなものがないためインスタンス化できませんが、これは有効です。この機能は、テンプレート メタプログラミング ツールとしてよく使用されます。
ただし、テンプレートの「インスタンス化」は、生成されるオブジェクト コードの量とは直接相関しないことに注意してください。これは、コンパイラ/リンカーの実装によって異なります。
コンパイラと設定に依存すると思います。たとえば、MSVC6 ではすべてが生成されたと思いますが、VS2005 では生成されません。仕様では、コンパイラはそうすべきではないと述べていますが、現実の世界では、実際のコンパイラに依存しています (たとえば、MSVC6 のブーストには多くの回避策があります)。/opt:ref が有効になっている場合、リンカは参照されていない関数を削除できます (VS の場合、他のコンパイラには同等のオプションが存在します)。
通常、はい。
コンパイラが本当に知っているのは、プログラムが各クラスの少なくとも 1 つのインスタンスを作成できるということだけです。しかし、それらのインスタンスで何をするかはわかりません。したがって、コードはほぼ確実に生成されます。
とはいえ、問題のメソッドが仮想ではなく、呼び出されない場合、リンカは通常のデッド コード削除機能を使用してそれらを削除できます。したがって、生成された (およびコンパイルされた) コードは、最終的な EXE には含まれません。
また、これはすべて同じではないため、使用されている C++ コンパイラに大きく依存します。