前回の記事において、あるクラスとその派生クラスに対してテンプレートの特殊化を適用させる方法を示しました。
継承元が普通のクラスである場合は 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
47
48
49
50
51
52
53
|
-
!
-
!
-
!
-
!
-
!
-
!
-
|
-
|
!
!
-
|
!
-
|
-
|
!
!
-
!
-
|
|
|
|
|
|
!
| #include <boost/type_traits/is_base_of.hpp>
#include <boost/utility/enable_if.hpp>
#include <iostream>
template<class T>
class Base { };
template<class T>
class Derived : public Base<T> { };
class DerivedBool : public Base<bool> { };
template<class T, class = void>
struct Func
{
void operator()() const
{
std::cout << "Normal" << std::endl;
}
};
#if 0
template<class T>
struct Func< T, ??? >
{
void operator()() const
{
std::cout << "Special" << std::endl;
}
};
#endif
int main(int, char**)
{
Func<int> a; a(); Func< Base<char> > b; b(); Func< Derived<int> > c; c(); Func<DerivedBool> d; d();
return 0;
}
|
解法は(私の頭では)2種類考え付きます。
Base<T>
クラスが変更可能である場合は簡単で、 Base<T>
クラスを更に別のテンプレートでないクラスから継承させれば済みます。
継承の大元がテンプレートクラスでなくなってしまえば、前回の記事の手法がそのまま使えます。
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
|
-
!
-
!
-
!
-
!
-
!
-
!
-
!
-
|
-
|
!
!
-
!
-
|
-
|
!
!
-
!
-
|
|
|
|
|
|
!
| #include <boost/type_traits/is_base_of.hpp>
#include <boost/utility/enable_if.hpp>
#include <iostream>
class BaseT { };
template<class T>
class Base : public BaseT { };
template<class T>
class Derived : public Base<T> { };
class DerivedBool : public Base<bool> { };
template<class T, class = void>
struct Func
{
void operator()() const
{
std::cout << "Normal" << std::endl;
}
};
template<class T>
struct Func<
T,
typename boost::enable_if< boost::is_base_of<BaseT, T> >::type >
{
void operator()() const
{
std::cout << "Special" << std::endl;
}
};
int main(int, char**)
{
Func<int> a; a(); Func< Base<char> > b; b(); Func< Derived<int> > c; c(); Func<DerivedBool> d; d();
return 0;
}
|
Base<T>
クラスが変更不可能である場合、前回の記事の最初の解法でも用いた手法である「可変長引数関数による型の切り分け」を行う必要があります。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
-
!
-
!
-
!
-
!
-
!
-
-
!
|
-
|
-
!
|
-
!
|
|
|
|
|
-
!
|
|
-
!
|
!
!
-
|
!
-
!
-
!
-
!
-
|
-
|
!
!
-
!
-
|
-
|
!
!
-
!
-
|
|
|
|
|
|
!
| #include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/add_reference.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/detail/yes_no_type.hpp>
#include <boost/utility/enable_if.hpp>
#include <iostream>
template<class T>
class Base { };
template<class T>
class Derived : public Base<T> { };
class DerivedBool : public Base<bool> { };
namespace
{
template<class TDerived>
class BaseTDerivedChecker
{
private:
static boost::type_traits::no_type check(...);
template<class T>
static boost::type_traits::yes_type check(
const Base<T>&,
typename boost::enable_if<
boost::is_base_of<Base<T>, TDerived> >::type* = 0);
static typename boost::add_reference<TDerived>::type d;
public:
static const bool value =
(sizeof(check(d)) == sizeof(boost::type_traits::yes_type));
};
}
template<class TDerived>
struct IsBaseTDerived
:
public boost::integral_constant<
bool,
BaseTDerivedChecker<TDerived>::value>
{
};
template<class T, class = void>
struct Func
{
void operator()() const
{
std::cout << "Normal" << std::endl;
}
};
template<class T>
struct Func<
T,
typename boost::enable_if< IsBaseTDerived<T> >::type >
{
void operator()() const
{
std::cout << "Special" << std::endl;
}
};
int main(int, char**)
{
Func<int> a; a(); Func< Base<char> > b; b(); Func< Derived<int> > c; c(); Func<DerivedBool> d; d();
return 0;
}
|
BaseTDerivedChecker<TDerived>
テンプレートクラスでは、少なくとも1つの型 T
について型 TDerived
が次の要件を満たす場合のみ静的メンバ関数 check
のオーバロードが定義されます。
- 型
TDerived
の参照型が型 const Base<T>&
に変換可能である。
- 型
TDerived
が Base<T>
クラスまたはその派生クラスである。
いずれの型 T
についても型 TDerived
が上述の条件を満たさない場合、オーバロードは定義されません。
なお、前回の記事も含め、例示したコードは古いコンパイラではコンパイルエラーとなる可能性があります。
私自身は Visual C++ 2008 でコンパイルして動作確認しています。