コンパイル時の継承の判定
コンパイル時の継承の判定
問
コンパイル時に継承の判定が可能だろうか?
前提
継承とは
簡単に言えば、既存クラス(基底クラス)を利用してクラス(派生クラス)を作成
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に記載されています