コンパイル時の継承の判定
コンパイル時の継承の判定
問
コンパイル時に継承の判定が可能だろうか?
前提
継承とは
簡単に言えば、既存クラス(基底クラス)を利用してクラス(派生クラス)を作成
class Base{ }; //基底クラス class Derived:public Base{}; //派生クラス
コンパイル時に判定とは
プログラムの実行までおおよそ下記の手順をとる
ソースファイル作成⇒コンパイルとリンク⇒実行
ゆえに、実行時ではなく、早い段階のコンパイル時に継承の判定ができればよし
参考までに、dynamic_castを利用すれば、実行時の継承の判定は可能。コストがかかるそうだが
解決
1.関数のオーバーロード
型Tが型Uへ型変換される型を受け取るものとそれ以外のもの
//戻り値で判定するため、絶対にサイズが違う型をつくる typedef char Small class Big{ char dumy[2]; }; //Uへ型変換される型を受け取る関数 Small Test(U); //ただしこの場合、Tのコンストラクタがprivate等だった場合、一時オブジェクトが構築されない可能性がある //ゆえに戻り値T型の関数を作成⇒定義をする必要がなし T MakeT(); Small Test(MakeT()); //U型(Uに型変換可能な型)以外の引数をとる関数。これがないとエラーがでてしまう Big Test(...);
2.sizeof
型Tが型Uに変換されたかどうかをsizeofを使って判定
const bool exists = sizeof(Test(MakeT()))== sizeof(Small); //sizeofの引数の関数Testは、定義を必要としていない
3.ポインタの暗黙的変換
ポインタとポインタの暗黙的な型変換ができるのは下記の時
1.voidへの型変換
int a = 1; int * ap = &a; void* av=ap;
2.基底クラスへの型変換
Derived dp; Base bp = &dp;
詳細:規格
以上をまとめたソースコード
template<class T , class U> class Conversion{ typedef char Small; class Big{ char dummy[2]; }; static T makeT(); static Small Test(U); static Big Test(...); public: enum { exists = sizeof( Test(MakeT()))==sizeof(Small)}; // enumハックを利用 enum { sameType=false}; }; // TとUが同じ場合 template<class T> class Conversion<T , T>{ public: enum{ exists = true }; enum{ sameType = true }; }; //まとめ #define SUPERSUBCLASS(T , U) \ ( Conversion<const U*,const T*>::exists && \ !Conversion<const T*,const void*>::sameType)
なるほど!
<参考>
上記テクニックはModern C++ Designに記載されています
テンプレート関数の部分的特殊化
テンプレート関数の部分的特殊化
問
テンプレート関数において、特定のテンプレートパラメータの時、当初のテンプレート関数と違う処理をしたい場合、どうすればよいのだろうか?
前提
- 部分的な特殊化はクラスの場合可能
//元となるクラステンプレート template<typename T, typename U> class Myclass{ }; //TがFoo の場合、元のクラステンプレートとは別の処理をしたい template<typename U> class Myclass<Foo, U>{ //別処理 };
- 関数だと部分的特殊化(注)はできない。
注 全てのテンプレートパラメータを指定する明示的な特殊化は可
//元 template<typename T, typename U> T* Create(const U& arg){ return new T(arg); } // TがFoo型の場合、元と別の処理がしたい template<typename U> Foo* Create<Foo, U>(U& arg){ return new Foo(arg, 1); } //このコードは許されていない。
解決
- 関数のオーバーロード利用する
- 型情報を伝達できる型の生成
//元 template<typename T, typename U> T* Create(const U& arg, T){ return new T(arg); }; //TがFooの場合特別な処理 template<typename U> T* Create(const U& arg, Foo){ return new Foo(arg, 1); };
ただし、これではT、Fooの一時変数が発生していしまい、オーバーヘッドに!
ここでオーバーヘッドが発生せず、型情報を伝える方法が必要
// 型情報を伝えるためのclass template<typename T> class Type2Type{ typedef T Originaltype; }; //元 template<typename T, typename U> T* Create(const U& arg,Type2Type<T>){ return new T(arg); } // UがFooの場合、特別な処理 template<typename T> Foo* Create(const U& arg, type2Type<Foo>{ return Foo(arg, 1); } //Createの使用方法 //元のCreateを利用 std::string pStr=Create("Hello", Type2Type<std::string>()}; //FooのCreateを利用 Foo* pF=Create(100, Type2Type<Foo>()};