-PR-
解決済み

テンプレートについて

  • 暇なときにでも
  • 質問No.40703
  • 閲覧数130
  • ありがとう数5
  • 気になる数0
  • 回答数3
  • コメント数0

お礼率 44% (11/25)

最近テンプレートを勉強し始めました。
試しに次のような関数を書いたのですがコンパイルエラーが出ます。

template<class T>
void pirntAll(T t)
{
T::iterator p;
for(p=t.begin();p!=t.end();p++)
cout<<*p<<" ";
cout<<endl;
}

エラーメッセージを見るとT::iterator p;のところがダメらしく
pが定義されていないと叱られます。
結局本などを参考にして次のように書き換えました。

template<class InputIterator>
void printAll(InputIterator first,InputIterator last)
{
while(first!=last)
{
cout<<*first<<" ";
first++;
}
cout<<endl;
}

しかしprintAll()を使うとき1つめの定義ならprintAll(x);と書けますが
2つめの定義だとprintAll(x.begin(),x.end());と書かなくてはならないので
面倒です。そこで2つめの定義と次の関数を組み合わせることで、コンパイルも通り、
使うときもprintAll(x);と書けるようにしました。

template<class T>
void printAll(T t)
{
printAll(t.begin(),t.end());
}

一応問題は解決したのですが何かひどく冗長なことをやらされているようで
気分が悪いです。なんとか1つめのような書き方ができないものでしょうか。
または1つめの書き方が出来ない(T::iteratorが使えない)正当な理由が
あるなら教えてください。
通報する
  • 回答数3
  • 気になる
    質問をブックマークします。
    マイページでまとめて確認できます。

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

  • 回答No.3
レベル11

ベストアンサー率 58% (114/195)

>なので「もしかしたら T が int かもしれない」という理由でコンパイルエラーはでないと思います。

確かに通りました(^^;

>また「もしかしたらTにまずいクラスが渡されるかも知れない」と言う心配はテンプレートを書くとき必要なんでしょうか。
>そんなことを言われたら全てのデータ構造に共通の処理しか出来なくなってしまうと思うのですが。

もしかして汎用のtemplate関数を書くのではなかったのでしょうか?
であれば最初の問題である「T::iterator pが通らない」は凄く簡単にコンパイルが通るように出来ますが・・・
渡す側のクラスをAとするとAのクラス宣言内でtypedef A* iterator;
とかしてやればtemplate内でT::iteratorがA::iteratorに変換されてちゃんと通ります(VC++ver6で確認済み)
お礼コメント
nagata

お礼率 44% (11/25)

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++に変えようか、と思っています。
ありがとうございました。
投稿日時 - 2001-02-26 16:58:32
-PR-
-PR-

その他の回答 (全2件)

  • 回答No.1
レベル11

ベストアンサー率 58% (114/195)

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

お礼率 44% (11/25)

>という訳で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;
}
投稿日時 - 2001-02-23 18:29:59


  • 回答No.2
レベル11

ベストアンサー率 58% (114/195)

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

お礼率 44% (11/25)

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にまずいクラスが渡されるかも知れない」と言う心配はテンプレートを書くとき必要なんでしょうか。
そんなことを言われたら全てのデータ構造に共通の処理しか出来なくなってしまうと思うのですが。
投稿日時 - 2001-02-23 20:57:00
このQ&Aで解決しましたか?
AIエージェント「あい」

こんにちは。AIエージェントの「あい」です。
あなたの悩みに、OKWAVE 3,500万件のQ&Aを分析して最適な回答をご提案します。

関連するQ&A
-PR-
-PR-
こんな書き方もあるよ!この情報は知ってる?あなたの知識を教えて!
このQ&Aにはまだコメントがありません。
あなたの思ったこと、知っていることをここにコメントしてみましょう。

その他の関連するQ&A、テーマをキーワードで探す

キーワードでQ&A、テーマを検索する
-PR-
-PR-
-PR-

特集


専門家があなたの悩みに回答!

-PR-

ピックアップ

-PR-
ページ先頭へ