- ベストアンサー
VC++6.0でテンプレートクラスを用いたいのですがエラーが出ます
'CImageTemplate' : 指定の指定のテンプレート クラスはすでに定義されています と言われてしまいます. まだC++の知識が浅いのですが間違っている箇所がわかれば教えてください. ---------テンプレートのヘッダ(ImageTemplate.h)--------- template <class TempData> class CImageTemplate{ private: TempData **Data;//データ本体 … protected: … InitData(int h, int w); }; ---------テンプレートのcpp(ImageTemplate.cpp)--------- #include "stdafx.h" #include "ImageTemplate.h" template<class TempData>CImageTemplate<TempData>::InitData(int h, int w) {…} ---------呼び出し側のヘッダ(ImageLabel.h)--------- #include "ImageTemplate.h" class CImageLabel{ private: CImageTemplate < int > Label; … }; ---------呼び出し側のcpp(ImageLabel.cpp)--------- #include "stdafx.h" #include "ImageLabel.h" #include "ImageTemplate.h" CImageLabel::InitLabel(int h, int w) { if(!Label.CheckData()){ Label.InitData(h, w); } }
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
クラスが多重定義である旨のエラーが出るのは、ANo.2、ANo.3 の方のおっしゃるとおり、いわゆるインクルードガードが無いのが原因です。 まぁ、それはそれとして、それよりも大事な ANo.1 の方の回答を補足すると、要するにテンプレートクラスの定義は *.h の中に書くべきものであり、*.cpp に書いてはダメということです。 もちろん、*.h は最後にはどれかの *.cpp にインクルードされてコンパイルされるわけであり、インクルードするということは *.cpp の中に直接記述するのと同じですから、*.cpp の中に書いても当然コンパイルは可能なわけで、これは違反でもなんでもないわけですが、次の理由で意味が無いのです。 コンパイルの過程は A.cpp をコンパイルして A.obj をつくり、次にその A.obj をリンクして実行形式 A.exe をつくるという具合に進みますが、テンプレートクラスの定義だけを例えば A.cpp に書いてコンパイルしても、テンプレート引数の型が未定なので、中間ファイルである *.obj を作りようが無いのです。これは、何かの入れ物を作ってくださいと言われたけれど、何を入れるのか不明な状態では、どのぐらいの大きさのどんな形の入れ物を作れば良いのかわからないというのに似たような状況です。 なので、普通は、テンプレートクラスの定義は全て *.h の中に記述します。 *.h が *.cpp にインクルードされて、そこでテンプレート引数の型を与えられてクラスオブジェクトが具現化されて始めて *.obj が作成可能になるのです。 テンプレートクラスのメンバ関数も普通はクラスの内部に記述します。 例えば、 template <class T_param> class CSomething { public: void MemberFunc() {} // クラスの中で定義する。 }; というような感じになります。 上記の MemberFunc() は次のようにクラスの外(ただし、ヘッダの中)に定義することも可能ですが、とても冗長な記述になるので普通はこのようにはしません。 template <class T_param> class CSomething { public: void MemberFunc(); // プロトタイプ宣言をして、 }; template <class T_param> void CSomething<T_param>::MemberFunc() {} // クラスの外で定義する。 健闘を祈ります。
その他の回答 (3)
- machongola
- ベストアンサー率60% (434/720)
No2の方と同じく#ifdefガードがヘッダーに無いためだと思います。 ヘッダーとソースに分割されたクラステンプレートを実際に使用するためにソースに向かってクラステンプレート側ソースをインクルードする必要があります。 この場合ImageTemplateクラスを使用するソースに向かってImageTemplate.cppをインクルードする事になります。 ImageTemplate.cppに変更が加わるとインクルードした先をコンパイルし直しますが、インクルードした個所が複数に及ぶと、影響個所を検出できないのでめちゃくちゃなコードを作成される可能性があります。 それを避けるため変更の度にリビルドをかけるしかありません(VC6以外は知りません)。 上記の理由により、クラステンプレートの分割は可読性が高いものの、あまりお勧めできません、 その他にもVC6はテンプレートをコンパイルする能力に関しては沢山の問題を抱えています。
- redfox63
- ベストアンサー率71% (1325/1856)
ImageTemplate.hを2回インクリュードしているためのエラーだと思います ImageLabel.cppにおいてImageLabel.hをインクリュードしています このインクリュードされたImageLabel.hでImageTemplate.hがインクリュードされます cpp側で もう一度 ImageTemplate.hをインクリュードしていますからクラス宣言が重複します #ifdef/#defeine/#endifディレクティブによる番兵を使うか #pragma onceディレクティブで複数回のインクリュードを抑制しましょう
- MrBan
- ベストアンサー率53% (331/615)
template<class TempData>CImageTemplate<TempData>::InitData(int h, int w) VC6でtemplateを分割コンパイルすることはできません。 ImageTemplate.h内でインライン定義してみてください。 (FAQだと思いますがC++のtemplateは現実的にそういうものです)
お礼
大変お礼が遅くなって申し訳ありませんでした. まだまだC++は勉強不足だということがわかりました. みなさんの回答大変参考になりました.ありがとうございました.