テンプレート引数に自身を継承したクラスのみ受け付ける方法
昨日のトピックの例のように、継承して自身を基底クラスのテンプレートに渡すというタイプのライブラリはしばしば見かけます。
//あるライブラリにおいて・・・ template< class T > class Library; //継承してから自分自身をテンプレート引数に渡す class MyClass:public Library< MyClass >
こういったライブラリの場合、テンプレートに異なるクラスを渡されたら困ります。なのでその場合にわかりやすいエラーが返ってくることが望ましいです。
C++11からtype_traitsというライブラリが入りました。最初、これを見たとき一体何に使うのか全く分からなかったのですが(いや今でもよくわかってないですが)今回のような場合にとりあえず使えます。
リファレンスを調べたらありました、継承関係にあるかどうかを調べるライブラリが。
http://www.cplusplus.com/reference/type_traits/is_base_of/
こいつを使えばわかりやすいエラーを返せるような実装ができそうです。
template< class T > class Library { static_assert( std::is_base_of< Library, T >::value, "template T must be base of this" ); };
これで適切なコンパイルエラーを返すことができるようになりました・・・。と思ったのですが正しく使っても出るようです。
error C2139: 'MyClass' : 定義されていないクラスは、コンパイラの組み込み型の特徴である '__is_base_of' への引数として使用できません
VisualStudioでの例ですがおそらく他のコンパイラでも同様かと思います。
要するに自作のクラスMyClassは少なくともこの時点(is_base_of)では不完全でありますよと。確かに継承の最中なので不完全でしょうね。というわけで他の実装方法が必要なのですが・・・。あまり綺麗ではないかもしれないがコンストラクタに入れるという策をとりました。
template< class T > class Library{ public: Library(){ static_assert( std::is_base_of< Library, T >::value, "template T must be base of this" ); } };
これでコンストラクタを呼ばれる場所まで評価されないので継承先のクラスも完成した後なので問題がない。
ただしこれ別の問題があって、コンストラクタが呼ばれるような実装でないと評価されない、つまり、継承先のクラスを実際に使うような実施になっていなければテンプレート引数が間違っていてもエラーを返しません。
自作のクラスで使う分には問題ないでしょうが、さらにライブラリを作る場合は問題になる場合があるかもしれませんね。