仮想関数のテンプレート化に対するテクニックはあるか

このQ&Aのポイント
  • 仮想関数をテンプレートにできないため、型ごとにメンバ関数を記述する必要がある
  • テンプレート仮想関数を実現する方法はないが、メンバ関数を記述する手間を減らす方法はある
  • 複雑でも型ごとのメンバ関数を入力する手間を省くためのテクニックを探している
回答を見る
  • ベストアンサー

テンプレート仮想関数のようなもの

仮想関数をテンプレートにできないのは仕様と理解しているのですが、多少複雑になっても、それぞれの型に対するメンバ関数をいちいち記述していくことを回避するようなテクニックはありませんか? struct HOGE{ //template<typename T> //virtual operator T() = 0; //上記ができないので以下を全て記入していかなければならない virtual operator int() = 0; virtual operator short() = 0; //... }; struct HOGE1 : HOGE{ //template<typename T> //operator T() overtride{return T();} //上記ができないので以下を全て記入していかなければならない operator int(){return 0;} operator short(){return 0;} //... };

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

  • ベストアンサー
回答No.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; }

haporu
質問者

補足

回答ありがとうございます。 やはり、型が増えるごとに自分が何を保持しているかによるswitchまたはifを増やしていくしかなさそうですね。 長い間おつきあいいただきありがとうございました。

その他の回答 (3)

回答No.3

 こんばんは。補足頂きました。  そしたら、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; }

haporu
質問者

お礼

このコードだと、それぞれの型の変数を代入すると、それぞれの型の領域に格納されて それぞれの型への変換を求められると、それぞれの型の領域から取り出すようです。 intを代入してもstringにキャストしたときは""になってしまいます。 1つの領域に格納し、それぞれの型への変換を求められると、現在格納されている型から その型への変換をするという、variant型のような動作をさせるのが目的です。 stringの"123"を代入して、intにキャストしたら数値の123が出てくる必要があります。

haporu
質問者

補足

回答ありがとうございます。返事が遅くなってすいません。 いただいたコードでいろいろ試行錯誤中です。

回答No.2

 こんにちは。  クラステンプレートの多重継承でそれらしき事ができます。  実用レベルにする為に、本気で書くと結構面倒です。  更に新しい型をサポートする為には、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; }

haporu
質問者

お礼

いろいろいじってみましたが、やはり多重継承ではvariant型のunionのような1つの領域を共有することができませんでした。 struct HOGE :private type<int>, private type<char>, private type<std::string>//更にサポートさせたい型をtype<???>で継承して追加していく ここで必要な型を全て継承すると、それぞれの領域が作られて、異なる型のための領域が共有されなくなってしまいます。 型別に領域を設けて格納するのではなく、1つの領域に格納して、ある型への変換を求められたときは、現在格納されている型からの変換を行いたいです。

haporu
質問者

補足

ありがとうございます。返事が遅くなってすいません。 これを元にいろいろと改造していっているのですが、どれが入っているか実行時に判別したうえで変換するという部分で躓いてしまいました。

  • rinkun
  • ベストアンサー率44% (706/1571)
回答No.1

マクロを使えば? 例えば #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) //... }; とか。 イマイチやりたいことが見えないので、これで良いのか分かりませんけど。

haporu
質問者

補足

回答ありがとうございます。 やりたいことは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などをできるだけ記述しないような方法を探しています。

関連するQ&A

  • C++テンプレートクラスの内部クラスについて

    テンプレートクラスについていろいろ試していたところ以下のようなコードで struct A {     struct AA { };     operator A::AA() { return A::AA(); } // (1) }; template<typename T> struct B {     struct BB { };     template<typename U>     operator B<U>() { return B<U>(); } // (2)     template<typename U>     operator typename B<U>::BB() { return typename B<U>::BB(); } // (3) }; int main() {     static_cast<A::AA>(A()); // (1) ok     static_cast<B<int> >(B<short>()); // (2) ok     static_cast<B<int>::BB>(B<short>()); // (3) compile error     return 0; } (1)と(2)はできて(3)だけがコンパイルを通りませんでした。 試したコンパイラはVC9とg++(3.3.4)とbcc32(5.5.1)で、VC9では以下のようなエラーをはきました。 「error C2440: 'static_cast' : 'B<T>' から 'B<T>::BB' に変換できません。     with [ T=short ] and [ T=int ] コンストラクタはソース型を持てません、またはコンストラクタのオーバーロードの解決があいまいです。」 (1)と(2)ができれば(3)のようなこともできそうな感じがしたのですが、他に書き方があるのでしょうか。 どなたかご存知の方がいらっしゃいましたらご教示お願いします。

  • C++ template operator T()

    ソースを見ていて分からないところがあったので教えて下さい。 なんと説明すればいいのか分からないので下に要約したソースを書きます。 template <typename T> class Hoge { T a_; public: Hoge(T a = 0) : a_(a) {} operator T() const { // ココが分からない return a_; } }; 最初は、関数オブジェクトかと思ったのですがそうではないですよね? operator()(引数) ですよね? 次は、Tのコンストラクタかと思ったのですが、class Hoge の中に書くのも変な気がしました。

  • テンプレート指定クラスのコンストラクタを明示呼出

    自前でnewのような関数を作っています。 mallocで確保したメモリ領域のそれぞれの項目に対してコンストラクタを呼び出したいのですが、 テンプレートで指定されたクラスのコンストラクタを明示的に呼ぶにはどうしたらよいですか? テンプレート関数でなく、特定のクラスに対するものならできたのですが、 テンプレートで一般的に対応しようと思った所で躓きました。 また、deleteのほうは意図通り実装できました。 struct HOGE{ HOGE(){} }; //特定のクラスに対するものは正常 HOGE *mynew(size_t len){ HOGE *v = (HOGE*)malloc(len * sizeof(HOGE)); for(size_t t = 0; t < len; ++t){ //v[t].HOGE(); //error C2274: '関数形式のキャスト' : '.' 演算子の右側で使用できません。 v[t].HOGE::HOGE(); //OK } return v; } template<typename T> T *mynew(size_t len){ T *v = (T*)malloc(len * sizeof(T)); for(size_t t = 0; t < len; ++t){ v[t].T::T(); //error C2039: 'T' : 'HOGE' のメンバーではありません。 //代わりに以下のように書いてみたが若干危険で効率も悪そう T tmp = T(); memcpy(v + t, &tmp, sizeof(T)); memset(tmp, 0, sizeof(T)); } return v; } template<typename T> void mydelete(T *v, size_t len){ for(size_t t = 0; t < len; ++t){ v[t].~T(); //OK } free(v); } HOGE *h = mynew<HOGE>(3); //NG HOGE *j = mynew(3); //テンプレート関数でない方は呼べる mydelete(j, 3); //OK

  • 外部ファイルでのtemplate関数の実装方法

    外部ファイルでのtemplate関数の実装方法 sub.cppにtemplate関数を実装し、 main.cppで、sub.cppのtemplate関数を呼び出す、 みたいなことをやりたいのですが、 コンパイルは通りますが、リンクエラー?になってしまいます。 以下が上記のサンプルプログラムです。 //main.cpp #include <vector> #include <iostream> using namespace std; template <typename t_ret, typename t_array> t_ret sub_t(const t_array&); int main (int argc, char *argv[]) { vector<double> v; v.push_back(1.1); v.push_back(2.2); cout << sub_t<double, vector<double> >(v) << endl; return 0; } // sub.cpp #include <vector> using namespace std; template <typename t_ret, typename t_array> t_ret sub_t(const t_array& array) { return array[0]+array[1]; } //コンパイルログ main.o: In function `main': main.cpp:(.text+0x134): undefined reference to `double sub_t<double, std::vector<double, std::allocator<double> > >(std::vector<double, std::allocator<double> > const&)' collect2: ld returned 1 exit status make: *** [main] Error 1 どなたか、対処方法を教えてください。 宜しくお願いします。

  • テンプレート関数でコンパイルが通りません

    テンプレート関数でコンパイルが通りません いつもお世話になってます。テンプレート関数XXXで、引数のTYPE型によって、XXX内で呼び出す関数が変わります。以下の例では、eTypeがAならSetADataを呼び、eTypeがBならSetBDataを呼び出します。XXXのもうひとつの引数がテンプレート部です。本当は、rInfoの型(AInfoまたはBInfo)を判別できればeTypeは不要なのですが、判別の仕方がわからないので、eTypeでrInfoの型を伝えるようにしています。 template<typename T> int XXX( T& rInfo, const TYPE& eType ) if( eType == A ){   SetAData( rInfo ); else if( eType == B ){   SetBData( rInfo ); } // 明示的インスタンス生成 template int XXX<AInfo>( AInfo& rInfo, const TYPE& eType ) template int XXX<BInfo>( Binfo& rInfo, const TYPE& eType ) 上記のようにした時、コンパイラはTの部分をAInfoとBInfoの両方で解釈するので、どうしても以下のようなコンパイルエラーが出ます。 --- `int XXX(T&, const TYPE&) [with T = AInfo]': error: no matching function for call to `SetBData(AInfo&)' note: candidates are: int SetBData(BInfo&) `int XXX(T&, const TYPE&) [with T = BInfo]': error: no matching function for call to `SetAData(BInfo&)' note: candidates are: int SetAData(AInfo&) XXXをテンプレート関数でなく、明示的にAInfoとBInfoのオーバーロードにする手もあるかもしれませんが、XXXの中で、ここには載せていない大多数の部分は、まったく同じなので、テンプレート関数にするのが筋かなと思っています。このコンパイルが通るようにするには、どうすれはよいのでしょうか。

  • テンプレートクラスのフレンド関数の宣言

    テンプレートクラスに対して、operator << を定義しようとしてハマってしまったので。 ---- 最終的にできたコードはこんな感じ ---- template<size_t M> class MyContainer; template<size_t N> std::ostream& operator<<( std::ostream& os, MyContainer<N> const& cont ); template<size_t M> class MyContainer {  friend   ostream& operator<< <M>( ostream& , MyContainer<M> const& ); <= ここで「operator<<」としてハマった  public:   MyContainer() { }  private:   void Print_( std::ostream& os ) const {    copy( content_, content_ + M, ostream_iterator<int>( os, "\n" ) );   }  private:   int content_[M]; }; template<size_t N> ostream& operator <<( ostream& os, MyContainer<N> const& cont ) {  cont.Print_( os );  return os; } ------------------------------ テンプレートクラスのfriend関数を宣言する場合に、関数に明示的にテンプレート引数を与えないとテンプレート関数の特殊化だけが friend となるようです。 そういうもんだと言ってしまえばそれまでですが、何故こんな変態的な仕様になってるんでしょう? 特殊化された関数だけをテンプレートクラスのfriend に指定したいような状況が想像できません。

  • テンプレート関数

    template<typename Iterator, typename Type> void show(Iterator start, Iterator end) { ostream_iterator<Type> oi(cout, " "); copy(start, end, oi); } のように、関数の引数の型の数 (Iterator:1個)より、 テンプレートの数(Iterator,Type: 2個)が上回ってしまう場合、 この関数をどのように呼び出せばいいのでしょうか? 例えば、 vector<int> v; show(v.begin(), v.end()); ではエラーになります。

  • templateの使い方を教えて下さい。

    質問タイトルの通りです。 今、 unsigned char* AllocByteArray1d(unsigned long int n){ unsigned char *box; box = (uchar *)calloc(n, sizeof(uchar)); if(box == NULL){ puts("can't allocate memory..."); exit(1); } return box; } という関数があって、これはunsigned charの配列をとってくれる関数になってます。これをtemplateを使って、intの配列もとれるようにしたいんです。 恥ずかしながら試しに、 template <typename T> T* AllocByteArray1d(unsigned long int n){ T *box; box = (T *)calloc(n, sizeof(T)); if(box == NULL){ puts("can't allocate memory..."); exit(1); } return box; } とやってみましたがダメでした。事前にTのデータ型がわからないからだろうか、と思って template unsigned char* AllocByteArray1d(unsigned long int); を入れて実体化させてみましたが、これでもダメでした。 どこを修正すれば使えるようになるのか、C++に詳しい方に教えて頂けると幸いです。

  • テンプレートが複雑すぎる?

    以下のような文字列比較関数を作ろうと思っています。 ○配列変数であるときは配列版を、そうでないときはポインタ版を呼び出す  配列のサイズがわかるときは、仮に\0で終端していなくてもそれ以上検索しない ○charかwchar_tかを意識せずに使える ○charとwchar_tという記述ではなく、templateでできるだけジェネリック?的に記述したい //charを入れるとwchar_t、wchar_tを入れるとcharを返すメタ関数 template<typename T> struct invert{}; template<> struct invert<char>{ typedef wchar_t t; }; template<> struct invert<wchar_t>{ typedef char t; }; //同じ型同士の比較は省略 //A: 両方ポインタ template<typename T> bool compare(const T *const &v1, const typename invert<T>::t *const &v2); //B1: 左がポインタ、右が配列 template<typename T, size_t L> bool compare(const T *const &v1, const typename invert<T>::t (&v2)[L]); //B2: Tとtypename invert<T>::tを逆に template<typename T, size_t L> bool compare(const typename invert<T>::t *const &v1, const T (&v2)[L]); //C: 左が配列、右がポインタ //省略 //D: 両方配列 template<typename T, size_t L1, size_t L2> bool compare(const T (&v1)[L1], const typename invert<T>::t (&v2)[L2]); char aa[] = "abc"; char *pa = aa; wchar_t aw[] = L"abc"; wchar_t *pw = aw; bool c1 = compare(pa, pw); //A bool c2 = compare(pa, aw); //B1はC2784, B2なら可 bool c3 = compare(aa, pw); //C bool c4 = compare(aa, aw); //DはC2784 invert<T>::t (&v2)[L]という記述が複雑?なのかB1は呼べず、B2と記述すると通りました。 しかしDはどちらも配列であるため、B2のように回避できません。 オーバーロードを全て記述せずに、できるだけ簡単にすます方法はありませんか? 環境はVC++2010です。

  • C++テンプレートでの引数

     こんにちは。 今回は、C++ テンプレートで引数に構造体を使えないか悩んでいます。 例えば #include <stdio.h> typedef struct _rect{ int x; int y; }RECT; typedef struct _rect{ double x; double y; }DATA; template <calss T> T Function(T abc) { // 構造体の要素にも対応している。 cout << abc//の要素x << endl; cout << abc//の要素y << endl; return T; } int main() { RECT t; DATA r; t.x = 80; t.y = 90; t = Function(t); // こういった事をしたい r = Function(r); return 0; } というふうにしたいんです。 テンプレート関数に引数として構造体にしてそれぞれの要素を参照する場合はどうすれば よろしいのか教えてください。 よろしくお願いします。

専門家に質問してみよう