2

テンプレート化されたスーパークラスを使用して、C++ クラスを作成しようとしています。アイデアは、似たような特性を持つ多数のスーパークラスから、似たようなサブクラスをたくさん簡単に作成できるということです。

問題のあるコードを次のように抽出しました。

template_test.h:

template<class BaseClass>
class Templated : public BaseClass
    {
public:
    Templated(int a);
    virtual int Foo();
    };

class Base
    {
protected:
    Base(int a);
public:
    virtual int Foo() = 0;
protected:
    int b;
    };

template_test.cpp:

#include "template_test.h"

Base::Base(int a)
    : b(a+1)
    {
    }

template<class BaseClass>
Templated<BaseClass>::Templated(int a)
    : BaseClass(a)
    {
    }

template<class BaseClass>
int Templated<BaseClass>::Foo()
    {
    return this->b;
    }

main.cpp:

#include "template_test.h"

int main()
    {
    Templated<Base> test(1);
    return test.Foo();
    }

コードをビルドすると、シンボルTemplated<Base>::Templated(int)Templated<Base>::Foo()が見つからないというリンカ エラーが発生します。

main.cpp簡単なGoogleは、次を追加すると問題が解決することを示唆しています。

template<> Templated<Base>::Templated(int a);
template<> int Templated<Base>::Foo();

しかし、これでは問題は解決しません。行を追加してmain.cppも機能しません。(興味深いことに、それらを両方に追加すると、リンカーから「複数定義されたシンボル」エラーが発生するため、何かをしているに違いありません...)

ただし、すべてのコードを 1 つのソース ファイルに入れると、問題は解決します。これは上記の厄介な例では問題ありませんが、私が見ている実際のアプリケーションは、すべてを 1 つの cpp ファイルに入れることを余儀なくされた場合、非常に速く管理できなくなります。

私がやっていることさえ可能かどうか誰にも分かりますか? (どのように) リンカー エラーを解決できますか?

すべてのメソッドをインラインで作成でき、これでうまくいくと思いますがclass Templated、これも理想的ではないようです。

4

4 に答える 4

4

テンプレート化されたクラスでは、それを使用する各翻訳単位で定義が利用可能である必要があります。定義は別のファイルに入れることができ、通常は.inl拡張子.tccが . ヘッダーファイル#includeはそのファイルの下部にあります。したがって、別のファイルにある場合でも、#include翻訳単位ごとに d です。スタンドアロンにすることはできません。

したがって、あなたの例では、名前template_test.cpptemplate_test.inl(またはtemplate_test.tcc、または何でも) に変更し、インクルード ガードの直前#include "template_test.inl"の の下部に (または何でも) を配置します。template_test.h#endif

お役に立てれば!

于 2008-10-16T08:07:22.083 に答える
2

問題は、テンプレートファイルがコンパイルされるときに、コンパイラがコードを生成するために必要なタイプを認識しないため、認識しないことです。

次に、リンクすると、main.cppはそれらの関数が必要であると言いますが、それらはオブジェクトファイルにコンパイルされていないため、リンカはそれらを見つけることができません。

他の回答は、この問題を移植可能な方法で解決する方法を示しています。つまり、テンプレート化されたメンバー関数の定義を、そのクラスのインスタンスをインスタンス化する場所から見える場所に配置します。明示的なインスタンス化によって、または実装をmain.cppから#includeされたファイル。

また、コンパイラのドキュメントを読んで、設定がどのように推奨されているかを確認することもできます。IBM XLCコンパイラーには、これらのセットアップ方法についていくつかの異なる設定とオプションがあることを私は知っています。

于 2008-10-16T14:12:11.257 に答える
1

C++ FAQ-lite はこれをカバーし、いくつかの方法でそれを回避します。

すべてのメソッドを「インライン」にする必要はありませんが、メソッド本体は template_test.cpp ではなく template_test.h で定義する必要があります。

一部のコンパイラはこの分割を処理できますが、あるレベルでは、テンプレートはマクロのようなものであることを覚えておく必要があります。コンパイラが特定の のテンプレートを生成するには、テンプレート ソースが手元にある必要があります。

于 2008-10-16T08:11:24.683 に答える
-1

コンパイラが main.cpp をコンパイルしているとき、クラス定義にはメンバー関数宣言がありますが、メンバー関数定義はありません。「テンプレート化された」コンストラクターと Foo 実装の定義がどこかにある必要があると想定しているだけなので、リンク時にそれを見つけるためにリンカーに委ねます。

問題の解決策は、Templated の実装を template.h に入れることです。

例えば

template<class BaseClass>
class Templated : public BaseClass
    {
public:
    Templated(int a) : BaseClass(a) {}
    virtual int Foo() { return BaseClass::b; }
    };

興味深いことに、これを template_test.cpp の最後に置くことで、コードをリンクさせることができました。

void Nobody_Ever_Calls_This()
{
    Templated<Base> dummy(1);
}

これで、コンパイラはリンクする Templated のインスタンスを見つけることができます。テクニックとしてはお勧めしません。他のファイルが

Templated<Widget>

そして、別の明示的なインスタンス化を template_test.cpp に追加する必要があります。

于 2008-10-16T08:31:02.210 に答える