2

重複の可能性:
テンプレートを使用すると、「未解決の外部シンボル」エラーが発生するのはなぜですか?

少し複雑なクラスのセットがあります。
子がパラメータ化されたクラスAのクラス_Aには、2つの子A1とA2があります。
クラスB。メンバーとしてクラス_Aのオブジェクトへのポインタが含まれています。それぞれクラスA1とA2に対応する2つの子クラスB1とB2があります。B1は_AをA1として構築します。それぞれA2としてB2。
そして最後に、クラスB内で子BYが定義されているクラスY
です。これで、ファイルにどのように存在するかがわかります。

tf1.h

#include <iostream>

struct Y{ // goes to the file tf2.h as ancestor of the class B::BY
};


struct _A{ // goes to the file tf2.h as a member of the class B
};

template<class X>
struct A: public _A {  // goes to two classes below only as an ancestor
    virtual void method();
    protected:
    virtual void m() = 0;
};

template<class X>
struct A1: public A<X>{  // goes to the file tf2.h to the class B1
    protected:
    void m();
};

template<class X>
struct A2: public A<X>{  // goes to the file tf2.h to the class B2
    protected:
    void m();
};

tf1.cpp

#include "tf1.h"


template<class X>
void A<X>::method(){
    /* here the class X used */
    std::cout << "A::method called" << std::endl;
    m();
}

template<class X>
void A1<X>::m(){
    std::cout << "A1::m called" << std::endl;
}

template<class X>
void A2<X>::m(){
    std::cout << "A1::m called" << std::endl;
}

tf2.h

#include "tf1.h"

class B{   // is the counterpain of the class _A
    protected:
    class BY: public Y{
    };

    _A * mp_x;
};


class B1: public B{  // is the counterpain of the class A1
    public:
    B1(){mp_x = new A1<BY>; std::cout << "B::B is called" << std::endl;}
};

class B2: public B{  // is the counterpain of the class A2
    public:
    B2(){mp_x = new A2<BY>; std::cout << "C::C is called" << std::endl;}
};

tfmain.cpp

#include <stdlib.h>

#include "tf2.h"

int main (int,char**){
    B2 b2;
    system ("PAUSE");
}

そして最後に、問題。

d:>g++ -std=c++0x tfmain.cpp -o tfmain.exe
ccB2BnSP.o:tfmain.cpp:(.rdata$_ZTV2A2IN1B2BYEE[__ZTV2A2IN1B2BYEE]+0x8): undefined reference to `A<B::BY>::method()'
ccB2BnSP.o:tfmain.cpp:(.rdata$_ZTV2A2IN1B2BYEE[__ZTV2A2IN1B2BYEE]+0xc): undefined reference to `A2<B::BY>::m()'
ccB2BnSP.o:tfmain.cpp:(.rdata$_ZTV1AIN1B2BYEE[__ZTV1AIN1B2BYEE]+0x8): undefined reference to `A<B::BY>::method()

MinGWv4.7.2を使用しています

4

3 に答える 3

3

テンプレート化された関数定義を.cppファイル内に配置することはできません。ヘッダーファイルに配置する必要があります。これはテンプレートの奇妙な制限です。

これは、関数のテンプレートバージョンが使用されたときにのみ生成されるという事実と関係があります。したがって、テンプレート化された関数はまだ使用されていないため、事前にコンパイルすることはできません。そのため、すべてが生成されるわけではありません。

于 2012-11-28T19:47:52.183 に答える
3

簡単に言うと、テンプレート定義をcppファイルに入れることはできません(回避策はありますが、快適なものではありません)

その理由は、コンパイラは、テンプレートをインスタンス化するタイプを知る必要があるため、そのテンプレートを最初に呼び出した時点でのみテンプレートをインスタンス化するためです。

ここで、テンプレート定義を別のcppファイルに入れると、それは独自の変換ユニットに個別にコンパイルされます。テンプレートをインスタンス化する場合、コンパイラはどこを探しますか?

例えば

// Template.h
template <typename T>
class templateObj { ~templateObj(); };

// Template.cpp
#include "Template.h"
template <typename T>
templateObj::~templateObj() { /* delete stuff! */ }

// YourFile.cpp
#include "Template.h"
templateObj<int> myObj;

さて、コンパイラがこのコードをコンパイルするとき。2つの変換ユニットが生成されます。1つは、用Template.cpp、もう1つは。用YourFile.cppです。

Template.cpp何が何であり、何がその中にあるのかについての単一の手がかりがないことに注意してくださいYourFile.cpp。したがって、でテンプレートパラメータタイプYourFile.cppを使用したことを知る方法はありません。このため、結果のの変換ユニットでは、コンパイラはのデストラクタのインスタンス化されたバージョンを生成しません。templateObjintTemplate.cpptemplateObj

ここで、を見YourFile.cppてみましょう。コンパイラは、templateObjintを使用してインスタンス化していることを確認すると、の定義を探しますtemplateObj。を含めTemplate.hたので、コンパイラは、のデストラクタを宣言したことを認識しますtemplateObj。しかし、定義はどこにありますか?

コンパイラは、の定義を~templateObj()認識せず、この時点でどこを探すべきかを認識していません。したがって、それは単に保留し、リンクする正しいモジュールを検索するためにリンカーに渡されます。

ここに問題があります:

2つの変換ユニットでは、コンパイラが生成したばかりであり、の定義YourFile.oもありません。リンカは読み込みを行い、リンク先のデストラクタのそのバージョンがあることを期待しますが、他の唯一の変換ユニットにはありません。実際、何もありませんTemplate.otemplate<int>::~template()YourFile.otemplateObjTemplate.o

ならどうしよう?リンカは文句を言う必要があります...そしてそれはあなたが得るエラーメッセージです。


コードで何が起こったかについてもう少し詳しく説明します。

  • 2つの翻訳ユニットが生成されます:tf1.oおよびtfmain.o
  • tf1.oから生成されtf1.cpp、それに含まれるものは何でも。
  • tfmain.oから生成され、それが含まれるものはtfmain.cpp何でも。

では、何が含まれているのでしょうか。残りのコードについて何tf1.cpptfmain.cpp知っていますか?

tf1.cpp

  • #include "tf1.h"==>どの#include <iostream>..。

tfmain.cpp

  • #include "tf2.h"==> which #include "tf1.h"==>which #include <iostream>..。

何がtf1.cppありますか?それは何を知っていますか?

  • との宣言知っているY_A
  • のテンプレート宣言知っているAA1およびA2
  • 、、およびのさまざまなメソッドのテンプレート定義がありますAA1A2

何がtfmain.cppありますか?それは何を知っていますか?

  • 、、、およびの宣言知っていますBB1B2Y_A
  • のテンプレート宣言知っているAA1およびA2
  • 、、のさまざまなメソッドの定義がありますBB1B2
  • 持っている mainとのインスタンスB2

だから、今の質問は次のとおりです。

tf1.cppまたはについて何か知っていますtf2.hか?タイプでインスタンス化tfmain.cppするインスタンスを作成していることを知っていますか?B2A2BY

何かtfmain.cpp知っていtf1.cppますか?、またはのメソッドの定義を知っていますか?AA1A2

彼らはお互いを知らないので、コンパイラは翻訳ユニットのテンプレートクラスへの定義のコードを生成する方法がありませtf1.oん(その時点では、インスタンス化するB2へのインスタンスを作成していることさえ知りませんA2タイプ付きBY)。

そして最後に、リンカーはコードtfmain.oが要求しているものを見つける方法がありません。コードが存在しないためです。

于 2012-11-28T20:00:18.600 に答える
0

Xymostechがあなたの質問に答えましたが、この制限が奇妙であることに同意しません。;) これはあなたのためにこの問題を明確にするはずです。

于 2012-11-28T19:51:36.217 に答える