最近テンプレートを勉強し始めました。
試しに次のような関数を書いたのですがコンパイルエラーが出ます。
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が使えない)正当な理由が
あるなら教えてください。
No.3ベストアンサー
- 回答日時:
>なので「もしかしたら T が int かもしれない」という理由でコンパイルエラーはでないと思います。
確かに通りました(^^;
>また「もしかしたらTにまずいクラスが渡されるかも知れない」と言う心配はテンプレートを書くとき必要なんでしょうか。
>そんなことを言われたら全てのデータ構造に共通の処理しか出来なくなってしまうと思うのですが。
もしかして汎用のtemplate関数を書くのではなかったのでしょうか?
であれば最初の問題である「T::iterator pが通らない」は凄く簡単にコンパイルが通るように出来ますが・・・
渡す側のクラスをAとするとAのクラス宣言内でtypedef A* iterator;
とかしてやればtemplate内でT::iteratorがA::iteratorに変換されてちゃんと通ります(VC++ver6で確認済み)
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++に変えようか、と思っています。
ありがとうございました。
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にあってないのです
この回答への補足
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にまずいクラスが渡されるかも知れない」と言う心配はテンプレートを書くとき必要なんでしょうか。
そんなことを言われたら全てのデータ構造に共通の処理しか出来なくなってしまうと思うのですが。
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()なども実装してやる必要があるでしょう
この回答への補足
>という訳で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が見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C++のcinの動作 5 2023/02/26 00:13
- その他(プログラミング・Web制作) Texに詳しい方よろしくお願いいたします 2 2023/01/16 22:25
- その他(プログラミング・Web制作) pythonにおける単方向リストの実装について 4 2022/07/13 12:34
- 英語 「this is the/my first time~」の文法上の制約について 1 2023/04/06 09:48
- 数学 数学 平面ベクトルにおける「一次独立」の定義は 3つのベクトルの大きさが0でない。平行でない。 でし 3 2023/04/10 02:25
- C言語・C++・C# C++プログラミングコードにポリモーフィズムを取り入れ方を教えてください。 2 2023/06/09 11:17
- C言語・C++・C# C言語初心者 ポインタについて、お助けください、、 2 2023/03/15 23:50
- 英語 品詞に関する質問です US allies Australia and New Zealand hav 3 2023/07/10 15:02
- その他(プログラミング・Web制作) pythonのグローバル変数 2 2022/11/25 18:02
- C言語・C++・C# C言語 3 2022/10/04 15:07
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
ダイアログクラスのコントロー...
-
ウィンドウの背景に画像を配置する
-
既定のコンストラクタがない?
-
【C++】vectorについて
-
VC++ リストボックスにデータを...
-
MFCアプリのコマンドラインでパ...
-
VB.NETでのEnumの使用について
-
このコンパイルエラーの意味に...
-
CMainFrameクラスの使い方
-
C++ protectedにアクセス不可
-
派生クラスから基本クラスprote...
-
C#で、動的にサブクラスを取得...
-
System.Net.HttpWebRequest、も...
-
Visual C++ 6.0 でクラス表示が...
-
クラスのアドレスを引数として...
-
vba sendKeysを送る相手のソフ...
-
MFCのドラッグ&ドロップについて
-
Delphi6でVB.NETで作成のDLL呼出
-
C++では構造体がメンバ関数をも...
-
継承を重ねた場合のコストはど...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
FriendとPublicの違い。。。
-
既定のコンストラクタがない?
-
イベントにAddHandlerされてい...
-
クラスのアドレスを引数として...
-
ダイアログ表示時にチェックボ...
-
(UWSC) 「#32770」の意味わかり...
-
エディットコントロールのイベ...
-
エディットコントロールでEnter...
-
WindowsAPIのリストビューの...
-
CMainFrameクラスの使い方
-
DebugクラスとTraceクラスの違い
-
MessageBoxを継承したい
-
C++ protectedにアクセス不可
-
MFCアプリのコマンドラインでパ...
-
このコンパイルエラーの意味に...
-
MFCのドラッグ&ドロップについて
-
【ASP.NET】 独自で作成したク...
-
派生クラスから基本クラスprote...
-
ダイアログクラスのコントロー...
-
C#で通常のbuttonコントロール...
おすすめ情報