Subscribed unsubscribe Subscribe Subscribe

クラスの継承関係をコンパイル時に検出する

Modern C++ Designから.

template<class T, class U>
class Conversion
{
	typedef char Small;
	class Big{ char dummy[2]; };
	static Small Test(U);
	static Big Test(...);
	static T MakeT();
public:
	enum { exists = sizeof(Test(MakeT())) == sizeof(Small) };
};

int main(void){
	std::cout << Conversion<int,double>::exists << std::endl; //1
	std::cout << Conversion<char*,double>::exists << std::endl; //0
}

sizeof演算子コンパイル時にサイズを評価することを利用.BigとSmallはサイズが違えば何でもいいけど,こう書いておけば処理系に依らずに同じ結果を出してくれる.コンパイル時評価にしか使わないので,当然Test(U),Test(...)に実体は必要無い.
たとえばテンプレートクラスで,テンプレート引数の型情報に応じてコンストラクタの処理を変えたい場合なんかに使うことができる.

template<int i>
struct Int2Type{ enum { value = i }; };

class Base{};

template <class T>
class Hoge {
public:
Hoge(){ Foo(Int2Type<Conversion<T,Base>::exists>());}
void Foo(Int2Type<1>){/*TとBaseに互換性があるときはこちらで初期化*/}
void Foo(Int2Type<0>){/*それ以外のときはこちらで初期化*/}
};

部分特殊化と違って,メンバ関数の部分特殊化の問題をクリアできること,スケーラブルであることが嬉しい.でもキモいっちゃキモい.

追記

boost::is_convertibleというのがあるらしい.知らなかった…