• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:テンプレートについて)

テンプレートについて - テンプレート関数のコンパイルエラーと要約

このQ&Aのポイント
  • 最近テンプレートを勉強し始めました。試しに次のような関数を書いたのですがコンパイルエラーが出ます。
  • エラーメッセージを見るとT::iterator p;のところがダメらしくpが定義されていないと叱られます。結局本などを参考にして次のように書き換えました。
  • しかしprintAll()を使うとき1つめの定義ならprintAll(x);と書けますが2つめの定義だとprintAll(x.begin(),x.end());と書かなくてはならないので面倒です。そこで2つめの定義と次の関数を組み合わせることで、コンパイルも通り、使うときもprintAll(x);と書けるようにしました。

質問者が選んだベストアンサー

  • ベストアンサー
  • alfeim
  • ベストアンサー率58% (114/195)
回答No.3

>なので「もしかしたら T が int かもしれない」という理由でコンパイルエラーはでないと思います。 確かに通りました(^^; >また「もしかしたらTにまずいクラスが渡されるかも知れない」と言う心配はテンプレートを書くとき必要なんでしょうか。 >そんなことを言われたら全てのデータ構造に共通の処理しか出来なくなってしまうと思うのですが。 もしかして汎用のtemplate関数を書くのではなかったのでしょうか? であれば最初の問題である「T::iterator pが通らない」は凄く簡単にコンパイルが通るように出来ますが・・・ 渡す側のクラスをAとするとAのクラス宣言内でtypedef A* iterator; とかしてやればtemplate内でT::iteratorがA::iteratorに変換されてちゃんと通ります(VC++ver6で確認済み)

nagata
質問者

お礼

VC++6では確かに通りました。!ありがとうございます。 実は今まで私が使っていたのはOSが Vine Linux2.0でコンパイラはgcc でやっていました。 その環境ではVC++でOKだったのと同じソースでコンパイルエラーを返して来ます。 ひょっとしてバージョンが古いとか,そういうことなのかもしれません。 ちなみにgccのバージョンは $gcc -v Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/specs gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) です。 この際、環境をWin&VC++に変えようか、と思っています。 ありがとうございました。

その他の回答 (2)

  • alfeim
  • ベストアンサー率58% (114/195)
回答No.2

templateはマクロではないのでプリプロセッサで置換されてからclassを評価するわけではなく、Tを汎用のものとして評価するはずです そのときT::begin()が出た場合、汎用のもの(プリミティブタイプ、クラス、構造体など)が出た場合、T::begin()はint::begin()にもなりうるわけです。 これって宣言できませんよね? なので設計の時点で間違ってるような気がします。 STLでは関数(Method)の内部に入ってからターゲットのiteratorを取る事はせず 引数で開始iteratorと終了iteratorを受け取るようにしているはずです。 なぜかというとtemplateの中では自分自身の宣言は参照できますが、汎用のタイプであるT::begin()は参照できません(Tがbegin()というメンバ関数を持つとは限りませんから) たぶん PrintAll()のtemplate内で typedef T* T::iterator; T::iterator T::begin() { // 適当に実装する; } T::iterator T::end() { // 同上 } とでもしてやればコンパイルは通るかもしれません(未確認です)が、上記のメンバ関数begin()が定義されているか分からないのと同様の問題でT::begin()も正しい実装を書く事は出来ないはずです。(Tにintが渡された時を考えてみてください。int.hogehogeってあるはずの無いものですから) という訳でPrintAll()関数の定義方法がSTLのiteratorにあってないのです

nagata
質問者

補足

Tがintにもなりうるからだめという説明がよくわかりません。 次のソースはOKでした。 class A { int a; static void print(){cout<<"Hello A"<<endl;} } class B { int b; static void print(){cout<<"Hello B"<<endl;} } template<class T> void print(T t) { T::print(); } main() { A a; B b; print(a); print(b); } 実行結果もちゃんと Hello A Hello B とでました。 なので「もしかしたら T が int かもしれない」という理由でコンパイルエラーはでないと思います。 また「もしかしたらTにまずいクラスが渡されるかも知れない」と言う心配はテンプレートを書くとき必要なんでしょうか。 そんなことを言われたら全てのデータ構造に共通の処理しか出来なくなってしまうと思うのですが。

  • alfeim
  • ベストアンサー率58% (114/195)
回答No.1

T::iteratorが出来ない理由ですが もし、templateにintなどのプリミティブタイプが渡された場合、 T::iteratorはどう解釈されるか考えてみてください たとえばintを渡した場合、T::iteratorはint::iteratorとおきかえられて解釈されます そのコードの前迄にint::iteratorが宣言されてなければ使えませんよね? (iteratorはC++の言語仕様ではなくSTLで実装されたものでありtypedefで置き換えられたポインタである事が多いようです) また、T::iteratorという表記ではTから置き換えられるnamespaceまたはTから置き換えられるクラス内の物でなければなりません さらにC++コンパイラでは  T::iterator p; という式は変数、またはクラス宣言として受け取られるためT::iteratorはクラス、またはプリミティブタイプでなければ構文的に不正なものとなってしまいます という訳でT::iterator以前に同じクラススコープ、またはnamespaceになる場所でtypedefでiteratorを宣言してやる必要があります あ、あと内部でiteratorを使うのだからそのクラス内でbegin(),end()なども実装してやる必要があるでしょう

nagata
質問者

補足

>という訳でT::iterator以前に同じクラススコープ、またはnamespaceになる場所でtypedefでiteratorを宣言してやる必要があります つまりどういうことでしょう? Tに渡すクラスの定義の中でiteratorを定義すればいいのでしょうか? 試しに次のようなクラスを作りましたがだめでした。 class Array10 { public: typedef int* iterator; int table[11]; iterator begin(){return table;} iterator end(){return table+10;} }; ちなみにmainは次のように書きました。 main() { Array10 a; for(int i=0;i<10;i++)a.table[i]=i; printAll(a); } printAllのなかで T::iterator が Array10::iterator に置き換わっているなら動きそうなものですが。 それともprintAllのなかでT::iteratorを使う前にtypedefで宣言する必要があると言うことでしょうか。 しかしTがなにか分かるまでtypedefできないと思うのですが。つぎのような感じでしょうか。 template<class T> void printAll(T t) { typedef ??? T::iterator; T::iterator p; for(p=t.begin();p!=t.end();p++)cout<<*P; cout<<endl; }

関連するQ&A