次のC++コードを実行してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | - ! - ! - ! - ! - ! - | - | ! ! - ! - | - | ! ! - ! - | | | | | ! | |
main 関数のコメントにも書かれている通り、 Base クラスから派生した Derived クラスについてもテンプレートの特殊化が適用されて欲しいわけですが、出力は次のようになります。
Normal Special Normal
このように、あるクラスに対してのテンプレートの特殊化はその派生クラスには適用されません。
では Base クラスおよびその派生クラスについて特殊化したい場合はどうするのかという話ですが、例えば次のようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | - ! - ! - ! - ! - ! - | | | | | | | | | | - ! ! - ! - ! - | - | ! ! - ! - | - | ! ! - ! - | | | | | ! | |
IsBaseOf テンプレートクラスは、 TDerived 型が TBase 型およびその派生型であるか否かを静的メンバ定数 value から得ることのできるクラスです*1。
オーバロードされている静的メンバ関数 check がミソで、 check(const TBase&) オーバロードを呼び出し可能であればそちらが、そうでなければ check(...) オーバロードが呼び出されます。
両者の戻り値の型の違いにより sizeof(check(d)) の値が変わり、それを sizeof(Yes) と比較することで TDerived 型が check(const TBase&) オーバロードを呼び出し可能な型であるか否かを調べています。
つまり IsBaseOf<Base, T>::value の値は、 T の型が Base クラスおよびその派生クラスであるか否かによって true もしくは false になります。
そしてその値が true であれば、テンプレートの部分特殊化が施された Func<T, true> がコンパイル時に生成される型として適用されるわけです。
…と、なんやかんや書きましたが、C++プラグラマにはおなじみの boost ライブラリを導入しているならばわざわざこんな風に書く必要はなく、次のように記述できます。
前述の例は汎用的に使おうとすると穴があったりするので、 boost ライブラリを導入済みなら迷わずこちらの書き方を用いましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | - ! - ! - ! - ! - ! - | - | ! ! - ! - | - | ! ! - ! - | | | | | ! | |
上記コード中の boost::enable_if 等について詳しくは次のサイトが参考になります。
本記事では派生元がテンプレートクラスである場合について論じていませんが、それについてはまたいずれ。