プロが教える店舗&オフィスのセキュリティ対策術

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

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が使えない)正当な理由が
あるなら教えてください。

A 回答 (3件)

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



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

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

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

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

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にあってないのです

この回答への補足

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
    • good
    • 0

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()なども実装してやる必要があるでしょう

この回答への補足

>という訳で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
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!