templateクラス

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
zxc
記事: 79
登録日時: 11年前
住所: 日本の背骨(?)あたり

templateクラス

#1

投稿記事 by zxc » 11年前

  1.テンプレートクラスを作ろうと思っています。一つ目の記述は分かりますが、二つ目の記述はどういうことになるのでしょうか。classのTとtypenameのTは別のものなのでしょうか。そもそも型の決定はどうすればよいのでしょう。

コード:

template<typename T>
class ABC{(Tを使う)};

コード:

template<typename T>
class T{(Tを使う)};
  2.Stateパターンのような構造を作ろうと考えています。今のところ考えているのは、切り替えることのできる状態を表現するのにとりあえず状態親クラス1つと状態子クラス複数、マネージャークラス等を考えています。そして今の考えでは、状態子クラスすべてが自身の型のポインタ型を返すReport関数を持つことになっています。
  

  返す型は規則的、関数の名前は子クラス同士で同じ、全ての子クラスがもつことなどから、

i.親クラスが純粋仮想関数という形で親クラスポインタ型を返すReportを持って、子クラスがオーバーライド、返り値をキャスト
ii.親状態クラスを継承したReportテンプレートクラスを、子クラスが継承、そのときに子クラスの型でテンプレートの型を決定

という二つを考えました。どちらも誤ったキャスト、型の決定によるバグが見つけにくいと思っています。
  今はiiの方法で進めていますが、よりよい方法や問題点等の指摘をお願いします。

コード:

//////iiのコード//////////////////
#include<iostream>
#include<typeinfo>


template<typename T>
class TempReport:virtual public weather{
	public:
		T* Report(){return (&T);}
};

class weather{};

class rain:public TempReport<rain>{
};

class sun:public TempReport<sun>{
};



int main(){
	using namespace std;

	sun s;
	rain r;

	cout<<typeid( s.Report() ).name()<<endl;
	cout<<typeid( r.Report() ).name()<<endl;

	return 0;
}




アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: templateクラス

#2

投稿記事 by h2so5 » 11年前

zxc さんが書きました:  1.テンプレートクラスを作ろうと思っています。一つ目の記述は分かりますが、二つ目の記述はどういうことになるのでしょうか。classのTとtypenameのTは別のものなのでしょうか。そもそも型の決定はどうすればよいのでしょう。

コード:

template<typename T>
class ABC{(Tを使う)};

コード:

template<typename T>
class T{(Tを使う)};
二つ目の記述はコンパイルが通りません。
http://ideone.com/emU8vC
zxc さんが書きました:   2.Stateパターンのような構造を作ろうと考えています。今のところ考えているのは、切り替えることのできる状態を表現するのにとりあえず状態親クラス1つと状態子クラス複数、マネージャークラス等を考えています。そして今の考えでは、状態子クラスすべてが自身の型のポインタ型を返すReport関数を持つことになっています。
  

  返す型は規則的、関数の名前は子クラス同士で同じ、全ての子クラスがもつことなどから、

i.親クラスが純粋仮想関数という形で親クラスポインタ型を返すReportを持って、子クラスがオーバーライド、返り値をキャスト
ii.親状態クラスを継承したReportテンプレートクラスを、子クラスが継承、そのときに子クラスの型でテンプレートの型を決定

という二つを考えました。どちらも誤ったキャスト、型の決定によるバグが見つけにくいと思っています。
  今はiiの方法で進めていますが、よりよい方法や問題点等の指摘をお願いします。
Report関数って何に使うんでしょうか?
「誤ったキャスト、型の決定によるバグが見つけにくい」という意味がよく分かりません。具体例を示してください。

zxc
記事: 79
登録日時: 11年前
住所: 日本の背骨(?)あたり

Re: templateクラス

#3

投稿記事 by zxc » 11年前

  下のコードもVC++2010では通るようです。ideoneで何故通らないかは分かりません。

コード:

 
#include<iostream>
#include<typeinfo>

class weather{};

template<typename T>
class T{};

int main(){
	using namespace std;
	return 0;
}


  Report関数について:簡単に纏めると現在の状態を型で判断しようと思っていて、現在の状態を知るために子クラスに自身の型を返させる(=Report関数)という方法をとったからです。
  (状態の切り替えはマネージャに全部任せるつもりです。マネージャは与えられた条件で状態を切り替えますが、切り替える前に変更後の状態と現在の状態が同じであれば切り替えません。今考えている設計が、変更後の状態はマネージャが他所から受け取り、現在の状態は子クラスからのReport関数で、親クラスのポインタへ受け取る設計だからです。)





  まず前提として、それぞれの子クラスにReport関数を記述するのは面倒なので、Report関数を他の方法ですべての子クラスに持たせることを考えています。

  誤ったキャストについては純粋仮想関数は間違いでただの継承です。すみませんでした。親クラスでReport関数をもって、それぞれのクラスの型のポインタにキャストする際に、別の子クラス型にキャストしてしまうとバグがあっても気づきにくいと言うことです。下ではtypeidの中でキャストしていないので、親クラスの型ポインタのままになります。それ以外にも問題多いのでこの方法は避けました。

コード:

#include<iostream>
#include<typeinfo>

class weather{ 
	public:
		weather* Report(){return (this);}
};

class rainy:public weather{	
};

class sunny:public weather{	
};


int main(){
	using namespace std;


	sunny s;
	rainy r;

	cout<<typeid( s.Report() ).name()<<endl;
	cout<<typeid( r.Report() ).name()<<endl;

	return 0;
}

  誤った型の決定については下のようにしてしまうと、自身の型ではないポインタを返せます。文法上誤りではないとおもうので気づきにくいと思うのです。

コード:

class weather{};

template<typename T>
class TempReport:virtual public weather{
	public:
		T* Report(){return (&T);}
};

class rainy:public TempReport<rainy>{・・・・・・};
class sunny:public TempReport<rainy>{・・・・・・};////templateはsunnyにすべき

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: templateクラス

#4

投稿記事 by h2so5 » 11年前

zxc さんが書きました:  下のコードもVC++2010では通るようです。ideoneで何故通らないかは分かりません。

コード:

 
#include<iostream>
#include<typeinfo>

class weather{};

template<typename T>
class T{};

int main(){
	using namespace std;
	return 0;
}
VC++のバグじゃないでしょうか。どっちにしろC++でこの構文は使えません。
zxc さんが書きました:   Report関数について:簡単に纏めると現在の状態を型で判断しようと思っていて、現在の状態を知るために子クラスに自身の型を返させる(=Report関数)という方法をとったからです。
  (状態の切り替えはマネージャに全部任せるつもりです。マネージャは与えられた条件で状態を切り替えますが、切り替える前に変更後の状態と現在の状態が同じであれば切り替えません。今考えている設計が、変更後の状態はマネージャが他所から受け取り、現在の状態は子クラスからのReport関数で、親クラスのポインタへ受け取る設計だからです。)
現在の状態と同じものが変更後の状態としてマネージャーに渡されるような設計自体がおかしいと思います。
zxc さんが書きました:   まず前提として、それぞれの子クラスにReport関数を記述するのは面倒なので、Report関数を他の方法ですべての子クラスに持たせることを考えています。

  誤ったキャストについては純粋仮想関数は間違いでただの継承です。すみませんでした。親クラスでReport関数をもって、それぞれのクラスの型のポインタにキャストする際に、別の子クラス型にキャストしてしまうとバグがあっても気づきにくいと言うことです。下ではtypeidの中でキャストしていないので、親クラスの型ポインタのままになります。それ以外にも問題多いのでこの方法は避けました。
ステートの管理は「親クラスの型ポインタのまま」使うのが普通であって、子クラス型にダウンキャストして使うのは設計がおかしいです。
それだったら継承を使っている意味がありませんよね。

ちなみにCスタイルキャストは危険なので使わないでください。
dynamic_castを使えば安全にダウンキャストをすることができます。
zxc さんが書きました:   誤った型の決定については下のようにしてしまうと、自身の型ではないポインタを返せます。文法上誤りではないとおもうので気づきにくいと思うのです。

コード:

class weather{};

template<typename T>
class TempReport:virtual public weather{
	public:
		T* Report(){return (&T);}
};

class rainy:public TempReport<rainy>{・・・・・・};
class sunny:public TempReport<rainy>{・・・・・・};////templateはsunnyにすべき
まず return (&T); がC++の文法として不正です。(定数のアドレスを取ることはできない)
VC++は不正な文法でもエラーを出さなかったりするので注意してください。

RTTIに頼るより、ユニークなidをenumなどでクラスごとにもってReportでそれを返すほうが素直な実装ではないかと。
このコードと同様にクラスに間違ったidを振ってしまう可能性もありますが、この手のミスをコンパイル時に判断させるのは限界があると思います。

zxc
記事: 79
登録日時: 11年前
住所: 日本の背骨(?)あたり

Re: templateクラス

#5

投稿記事 by zxc » 11年前

  class Tについてはバグという認識でいきます。

  あくまで構想に過ぎないので、他所から切り替えの判断上を受け取るのはおかしいと言われても、しっくりきません。しかしながら他所から切り替えの情報を受け取る例を考えたことはないですし、思いつかないです。必要なものだと思い込んだのでしょう。
  現在の状態と同じものが変更後の状態としてマネージャーに渡されるような設計自体
  これはちょっとよく分からないですが、多分他所から判断材料を得ようとする設計と関連する部分なのだと解釈します。自分としてはマネージャーは、「これから切り替えるべき次の状態」と「今の状態の」二つを持っている必要があると思うのです。なぜなら「これから切り替える状態」は子クラスから見えず、「今の状態」は窓口からは見えません。二つとも見えるのはマネージャーだけでマネージャーが状態を切り替え、更新します。外から判断材料を受け取るのがおかしいならば、一つにすべきだと自分も思います。

  キャストについては、今回はtypeidを使って型を見るだけにしていますが、本来は親クラスのポインタへいれるつもりです。代入前にキャストしてやらないとどの子クラスのReport関数も、親クラスのポインタ型を返してしまいます。


  型決定のコードはこうですね。すいませんでした。今回は型の決定は正しくしています。

コード:

#include<iostream>
#include<typeinfo>


template<typename T>
class TempReport:virtual public weather{
	public:
		T* Report(){return (this);}
};


class weather{};

class rainy:public TempReport<rainy>{
};

class sunny:public TempReport<sunny>{
};


int main(){
	using namespace std;


	sunny s;
	rainy r;

	cout<<typeid( s.Report() ).name()<<endl;
	cout<<typeid( r.Report() ).name()<<endl;

	return 0;
}


閉鎖

“C言語何でも質問掲示板” へ戻る