コンパイル時の継承の判定

コンパイル時の継承の判定

コンパイル時に継承の判定が可能だろうか?

前提

継承とは

簡単に言えば、既存クラス(基底クラス)を利用してクラス(派生クラス)を作成

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に記載されています

enum ハックはEffective C++の第3項に詳しいです