次の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
等について詳しくは次のサイトが参考になります。
本記事では派生元がテンプレートクラスである場合について論じていませんが、それについてはまたいずれ。