- ベストアンサー
typedef された型をテンプレートで見分ける方法
Windows 環境では幾つかの型(少なくないと思いますが)を typedef で定義されています。 例えば SNMP 類だと typedef HANDLE HSNMP_ENTITY; typedef HANDLE HSNMP_CONTEXT; 等。 こういった HSNMP_ENTITY や HSNMP_CONTEXT を扱うAPI群を幾つかのクラス化する場合、 基本処理を1つのテンプレートクラスで書こうとして、代入オペレータ operator=(const TYPE& h); を書いても TYPE を見分けてくれません(HSNMP_ENTITYとHSNMP_CONTEXTとを)。 typedef が弱い型付けである為、止む無いと考え、各末端のテンプレートクラスの派生クラス毎に書く事で妥協しました。 うまい手があればご教授願います。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
こんな感じにできませんか? template <class Traits> class Goo : public GooBase, public Traits { public: Goo& operator=(const Goo& other); ... }; typedef Goo<traits_1> Goo1; typedef Goo<traits_2> Goo2; ... typedef Goo<traits_1000> Goo1000; 全ての型に共通の部分をGooBaseで定義し、それぞれに固有のメンバ関数をTraitsとして提供すれば、実現できそうな気がします。 # TraitsよりPolicyといった方がいいかも...
その他の回答 (3)
- MrBan
- ベストアンサー率53% (331/615)
HGOO_1 h1 = ::CreateGoo1(...); HGOO_2 h2 = ::CreateGoo2(...); Goo1 goo1; Goo2 goo2; // ← [訂正]こぴぺミス…orz goo1 = h1; // ←こういう記述を許可すると goo1 = h2; // この誤認識を禁止できない goo1 = goo2; // ←これをコンパイルエラーにはできるので、 goo1 = Goo1(h1); // explicitにこうする。
- MrBan
- ベストアンサー率53% (331/615)
「生ハンドルからの代入演算子」を作りたい? それは、この場合1~1000のtypedefの実体がすべてHANDLE(言語的にはvoid*)なので、自動の型類推はできません。 > typedef が弱い型付けである為、止む無いと考え、 > 各末端のテンプレートクラスの派生クラス毎に書く事で妥協しました。 最終的に何らかの妥協は必要と思われます。 Goo1のoperator=に生ハンドルを渡せば、ソース上でHGOO_1であろうとHGOO_2であろうと同じHGOO_1のものが呼ばれます。 なので、このような代入演算子は書かないのが安全。 生ハンドルはコンストラクタのみで渡すようにし、コンストラクタは引数を複数にするか、explicit指定をつける。 手軽な変換を許すと代入で暗黙変換されて危険な典型だと思います。 コンストラクタのtraitsは結局プログラマが自分で指定せざるを得ない。 HGOO_1 h1 = ::CreateGoo1(...); HGOO_2 h2 = ::CreateGoo2(...); Goo1 goo1; Goo1 goo2; goo1 = h1; // ←こういう記述を許可すると goo1 = h2; // この誤認識を禁止できない goo1 = goo2; // ←これをコンパイルエラーにはできるので、 goo1 = Goo1(h1); // explicitにこうする。 実装の方向性としては#2さんの方法でいいと思いますが、元のコードの延長だとたとえばこんな感じでしょうか。 # コンパイルはしてません。VC6はもうMSのサポート切れですし。 // Type:templateはHGOO_1もHGOO_2も区別しないので渡しても意味がないが、 // (STRICTならHANDLE以外の)HDC等を渡すことはできるはず… template <typename Traits, typename Type=HANDLE> struct GooBase { typedef Type Type_type; ... }; struct Goo1 : public GooBase<Goo1> { explicit Goo1(Type_type handle); operator=(const Goo1& h); } // traits(=識別用の型)が本当に単なるマーカなら、typelistで多少はきれいになるのかも。 ちなみに、VC6だとtemplate実装が甘くて、メンバ関数の特殊化とか部分特殊化に制限/バグがあるので留意が必要です。
お礼
理想的なコードを回答頂き、有難うございます。 スッキリした気になりました。 >このような代入演算子は書かないのが安全。 そうでした... 見直して廃止できないか検討します。 >typelistで多少はきれいになるのかも。 最近、本でその存在を知ったばかりでして... これを機に応用したいと思います。
- jacta
- ベストアンサー率26% (845/3158)
やりたいことがよく分からないのですが、HSNMP_ENTITYとHSNMP_CONTEXTで、多重定義かテンプレートの特殊化をやりたいということでしょうか?
補足
ご返答有難うございます。 作戦としては、以下のような1000個のハンドルが有るとし、 typedef HANDLE HGOO_1; typedef HANDLE HGOO_2; ... typedef HANDLE HGOO_1000; これらに固有の関数群をクラスでラッパしたい考えです。 それらを Goo1~Goo1000 とします。 ここで各クラス化に於いて類似コードを基本クラスに置いた方が良いと考え、それをテンプレートクラスとしました。 template <class TYPE, class 識別用の型> class GooBase {...}; // class Goo1 : public GooBase<HGOO_1, 識別用の型> { operator=(const HGOO_1& h); // <--- ... }; ここで Goo1~Goo1000 にある代入オペレータ operator=(const TYPE& h); を GooBase へ置こうとしても機能してくれない点が難点です。 なお、コンパイラは VC6 です。
お礼
ご回答有難うございます。 掲示頂いたスタイルを参考に見直したいと思います。 なお、代入演算子については好ましくない指摘もありましたし、必要かどうか見極めたいと思います。 貴重な助言を頂き、有難うございました。