- ベストアンサー
仮想関数のテンプレート化に対するテクニックはあるか
- 仮想関数をテンプレートにできないため、型ごとにメンバ関数を記述する必要がある
- テンプレート仮想関数を実現する方法はないが、メンバ関数を記述する手間を減らす方法はある
- 複雑でも型ごとのメンバ関数を入力する手間を省くためのテクニックを探している
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
こんにちは。御礼と補足を拝見いたしました。 すんません、しばらく家を空けていました。 多重継承の使用は的外れでした。領域を共有するには、提示したソースの様な手段しか思い浮かびません。 但し、この手のマルチ変換は非常に手間のかかるものですので、直ぐに完璧な答えが出せるものではありません。 一応、int→std::string, std::string→intの変換を書いてみました。当方はこれにて、ギブアップとさせて頂きます。以下参考程度に。 #include<typeinfo.h> #include<iostream> #include<string> struct type_base { private: const type_info* tinfo; public: explicit type_base(const type_info* tinfo) : tinfo(tinfo) { } virtual ~type_base() { } virtual type_base* clone() const = 0; const type_info& get_type() const { return *this->tinfo; } }; template<typename T> struct type : public type_base { private: T value; public: explicit type(const T& v = T()) : type_base(&typeid(T)), value(v) { } type(const type& r) : value(r.value) { } ~type() { } type_base* clone() const { return new type<T>(this->value); } T get() const { return this->value; } }; struct HOGE { private: type_base* p; public: //NULLを防ぐ為int型で初期化 HOGE() : p(new type<int>(0)) { } HOGE(const HOGE& r) : p(r.p->clone()) { } template<typename U> explicit HOGE(const U& v) : p(new type<U>(v)) { } ~HOGE() { delete this->p; } HOGE& operator = (const HOGE& r) { if(this != &r) new (this) HOGE(r); return *this; } template<typename U> HOGE& operator = (const U& v) { if(typeid(U) != this->get_type()) { this->~HOGE(); new (this) HOGE(v); } return *this; } const type_info& get_type() const { return this->p->get_type(); } //int型へ変換 operator int() const { int result = 0; //自分がstd::stringを保有している if(this->get_type() == typeid(std::string)) { type<std::string>* value = static_cast<type<std::string>*>(this->p); result = ::atoi(value->get().c_str()); } return result; } //std::stringへ変換 operator std::string() const { std::string result; //自分がintを保有している if(this->get_type() == typeid(int)) { char ch[80]; type<int>* value = static_cast<type<int>*>(this->p); sprintf(ch, "%d\n", value->get()); result += ch; } return result; } }; int main() { HOGE hoge(int(123)); std::string str = (std::string)hoge; HOGE hoge2(std::string("123")); int i = (int)hoge2; return 0; }
その他の回答 (3)
- machongola
- ベストアンサー率60% (434/720)
こんばんは。補足頂きました。 そしたら、RTTIのtypeidを利用するのが手っ取り早いでしょう。 RTTIとtypeidに関しては、検索すれば沢山出てきます。 以前のソースに、ほんの少しだけ付け加える程度です。 後は、使用しているコンパイラに応じて、RTTIを有効にする為の設定をしておいて下さい。 以下参考程度に。 #include<typeinfo.h> #include<iostream> #include<string> template<typename T> struct type { private: T value; protected: explicit type(const T& v = T()) : value(v) { } type(const type& r) : value(r.value) { } ~type() { } type& operator = (const type& r) { if(this != &r) this->set(r.get()); return *this; } void set(const T& v) { this->value = v; } T get() const { return this->value; } }; struct HOGE : private type<int>, private type<char>, private type<std::string> { HOGE() : type<int>(), type<char>(), type<std::string>(), tinfo(&typeid(*this))//NULLを防ぐ為に自分の型情報を入れておく { } HOGE(const HOGE& r) : type<int>(r), type<char>(r), type<std::string>(r), tinfo(r.tinfo) { } HOGE& operator = (const HOGE& r) { if(this != &r) new (this) HOGE(r); return *this; } template<typename U> explicit HOGE(const U& v) { this->type<U>::set(v); this->tinfo = &typeid(v); } template<typename U> HOGE& operator = (const U& v) { this->type<U>::set(v); this->tinfo = &typeid(v); return *this; } const type_info& get_typeid() const { return *this->tinfo; } template<typename U> operator U() const { return this->type<U>::get(); } private: const type_info* tinfo; }; static void output(const HOGE& hoge) { if(hoge.get_typeid() == typeid(int)) { std::cout << (int)hoge << std::endl; } else if(hoge.get_typeid() == typeid(char)) { std::cout << (char)hoge << std::endl; } else if(hoge.get_typeid() == typeid(std::string)) { std::cout << (std::string)hoge << std::endl; } else { std::cout << "error" << std::endl; } } int main() { HOGE hoge; output(hoge); hoge = int(1); output(hoge); hoge = char('a'); output(hoge); hoge = std::string("test"); output(hoge); return 0; }
お礼
このコードだと、それぞれの型の変数を代入すると、それぞれの型の領域に格納されて それぞれの型への変換を求められると、それぞれの型の領域から取り出すようです。 intを代入してもstringにキャストしたときは""になってしまいます。 1つの領域に格納し、それぞれの型への変換を求められると、現在格納されている型から その型への変換をするという、variant型のような動作をさせるのが目的です。 stringの"123"を代入して、intにキャストしたら数値の123が出てくる必要があります。
補足
回答ありがとうございます。返事が遅くなってすいません。 いただいたコードでいろいろ試行錯誤中です。
- machongola
- ベストアンサー率60% (434/720)
こんにちは。 クラステンプレートの多重継承でそれらしき事ができます。 実用レベルにする為に、本気で書くと結構面倒です。 更に新しい型をサポートする為には、HOGEクラスにtype<???>の継承を追加すると同時に、HOGEクラスのコンストラクタ内にも、type<???>のイニシャライザを追加しなければいけません。 以下参考程度に。 template<typename T> struct type { private: T value; protected: explicit type(const T& v = T()) : value(v) { } type(const type& r) : value(r.value) { } ~type() { } type& operator = (const type& r) { if(this != &r) this->set(r.get()); return *this; } void set(const T& v) { this->value = v; } T get() const { return this->value; } }; struct HOGE : private type<int>, private type<char>, private type<std::string>//更にサポートさせたい型をtype<???>で継承して追加していく { HOGE() : type<int>(), type<char>(), type<std::string>() { } HOGE(const HOGE& r) : type<int>(r), type<char>(r), type<std::string>(r) { } HOGE& operator = (const HOGE& r) { if(this != &r) new (this) HOGE(r); return *this; } template<typename U> explicit HOGE(const U& v) { this->type<U>::set(v); } template<typename U> HOGE& operator = (const U& v) { this->type<U>::set(v); return *this; } template<typename U> operator U() const { return this->type<U>::get(); } }; static void output(const HOGE& hoge) { std::cout << (int)hoge << std::endl; std::cout << (char)hoge << std::endl; std::cout << (std::string)hoge << std::endl; } int main() { HOGE hoge; hoge = int(1); hoge = char('a'); hoge = std::string("test"); //コピーコンストラクタのテスト HOGE hoge2 = hoge; //代入演算子のテスト HOGE hoge3; hoge3 = hoge2; output(hoge); output(hoge2); output(hoge3); return 0; }
お礼
いろいろいじってみましたが、やはり多重継承ではvariant型のunionのような1つの領域を共有することができませんでした。 struct HOGE :private type<int>, private type<char>, private type<std::string>//更にサポートさせたい型をtype<???>で継承して追加していく ここで必要な型を全て継承すると、それぞれの領域が作られて、異なる型のための領域が共有されなくなってしまいます。 型別に領域を設けて格納するのではなく、1つの領域に格納して、ある型への変換を求められたときは、現在格納されている型からの変換を行いたいです。
補足
ありがとうございます。返事が遅くなってすいません。 これを元にいろいろと改造していっているのですが、どれが入っているか実行時に判別したうえで変換するという部分で躓いてしまいました。
- rinkun
- ベストアンサー率44% (706/1571)
マクロを使えば? 例えば #define VOPDEC(type) virtual operator type() = 0 struct HOGE{ VOPDEC(int); VOPDEC(short); //... }; #define VOPDEF(type) operator type(){return 0;} struct HOGE1 : HOGE{ VOPDEF(int) VOPDEF(short) //... }; とか。 イマイチやりたいことが見えないので、これで良いのか分かりませんけど。
補足
回答ありがとうございます。 やりたいことはvariant型のようなものを作ることです。 現在何の型を保持しているかという情報をvtblに任せるという方法をとってみようと思っています。 template<typename T = void> struct aconv; //基底 template<> struct aconv<void> abstract{ //ここでエラー template<typename U> virtual operator U() const = 0; }; //intなどの型用 template<typename T> struct aconv : aconv<>{ T v; aconv(const T &r) : v(r){} template<typename U> operator U() const{ return (U)v; } template<> operator str::string() const { /*数値から文字列への変換は後で実装*/ } //その他の型への変換 }; //特殊化1 template<> struct aconv<void*> : aconv<>{ typedef void *T; T v; aconv(const T &r) : v(r){} template<typename U> U conv() const{ return (U)(unsigned int)v; } }; //その他の特殊化(stringなど) template<> struct aconv<void*> : aconv<>{ ・・・ }; struct any{ //何らかのaconv<?>へのポインタ aconv<> *p; ~any(){ delete p; p = 0; } any() : p(0){} //ある変数の型のaconv<?>を確保してpに保持 template<typename T> any(const T &r) : p(new aconv<T>(r)){} template<typename T> operator T() const{ if(p){ return p->operator T(); }else{ return T(); } } //保持しているpを破棄して新しいaconv<?>を確保 template<typename T> any &operator=(const T &r){ aconv<> *tp = p; p = new aconv<T>(r); delete tp; return *this; } }; any a = 3; //代入は問題なくできるようになりました double b = a; // std::string c = a; // 仮想でないテンプレートoperator Uからテンプレートでない仮想関数などを呼び出して、どうにかして子クラスのテンプレート関数を呼ぼうかと思っていますが、型の情報をそのまま渡す手段がなくて、型を数値に変換して、数値から型を復元しようかと思いましたが、switch caseが多くなりすぎて困っています。 単純に変換を喜寿すると、caseの数が型数の2乗のオーダーになってしまうので、何とか型数の1乗のオーダーにして、特殊化が必要なstringなどを除いて、intやshortなどをできるだけ記述しないような方法を探しています。
補足
回答ありがとうございます。 やはり、型が増えるごとに自分が何を保持しているかによるswitchまたはifを増やしていくしかなさそうですね。 長い間おつきあいいただきありがとうございました。