web-dev-qa-db-ja.com

クラス本体外のテンプレート定義

大丈夫ですか。クラステンプレートの仮想関数をその本体の外側で定義するには?仮想関数をインライン化することはできませんが、コンパイル単位での複数の定義を避けるために、それらはinlineとマークされます(テンプレートヘッダーが複数のソースファイルに含まれると想定)。一方、コンパイラはinlineを自由に無視できるため、これは有効と思われます。例として、以下のコードは正しいですか?

_template <typename T>
class C
{
public:
    virtual void f(T val);
};

template <typename T>
inline
void C<T>::f(T val)
{
  //definition
}
_

ところで、gcc(3.4.2)では、関数f(T val)の定義の前にinlineを省略できますが、通常のクラスの類似関数の前には省略できません。それはgccの振る舞いだけですか?

14
doc

はい、inlineがなくても問題ありません。通常のメンバー関数と静的変数でも同じように機能します。

// everything in the header:
template <class T>
class A
{
  static int i;
};

template <class T>
int A<T>::i=0;

標準見積もり:(3.2/5)

クラスタイプ(第9項)、列挙型(7.2)、外部リンケージを使用したインライン関数(7.1.2)、クラステンプレート(第14項)、非静的関数テンプレート(14.5.6)には複数の定義があります。 、クラステンプレートの静的データメンバー(14.5.1.3)、クラステンプレートのメンバー関数(14.5.1.1)、または一部のテンプレートパラメータが指定されていないテンプレートの特殊化(14.7、14.5.5) )プログラム内で、各定義が異なる翻訳単位で表示され、定義が次の要件を満たしている場合...

要件は基本的に、2つの定義が同一でなければならないと言っています。

通常のクラスの場合は機能しません。プログラム全体で最大で1つの定義が必要です。

11
jpalecek

classを使用せずに、また複数の定義エラーを受け取らずに、同じヘッダーでinline定義の外側にテンプレートメソッドを定義できます。

これは、テンプレート関数が完全に特殊化されていない場合、定義自体を生成しないためです。私の主張を証明するために、次のことを行います。

void C<int>::f(int)
{
}

この場合、関数には定義があるため、リンカーエラーが発生します。 (これを複数の翻訳単位に含める場合。インラインでマークする場合:

inline void C<int>::f(int)
{
}

エラーは発生しなくなりました。

5
Luchian Grigore

問題の関数をインスタンス化する必要のあるコードが、コンパイル時(リンク時ではなく)にそのコードを可視化できる限り、そこで関数を定義できます。

テンプレート化されていない関数とその実装の場合と同様に、テンプレートを2つのファイルに分割することは非常に一般的です。1つは従来のヘッダーで、もう1つは実装です。唯一の違いは、使用するときにテンプレート実装ファイルとヘッダーを#includeする必要があることです。

4
Kylotan