複数のテンプレート引数を持つ場合の特殊化
やりたいこと
以下のようなテンプレート引数を2つ取るテンプレートクラスがあったとする.
#include <stdio.h> template<typename A, typename B> class XXXX { public: void func() { printf("General\n"); } int func2() { return 100; } };
ここでテンプレート引数 A が int の時だけ,func() の動作を変えたい.つまり特殊化したい.
というのやりたいこと.
方法1 (コンパイルエラー)
テンプレート引数Bを残したまま,Aだけをintに特殊化した関数を付け加えてみる.
template<typename B> inline void XXXX<int, B>::func() { printf("Special\n"); }
これは以下のようエラーが出てコンパイルエラーになる.
error: invalid use of incomplete type ‘class XXXX<int, B>’
方法2 (エラーとならないが汎用性が失われる)
方法1にテンプレート引数Bも明示的に与えるとコンパイルに通る.
template<> inline void XXXX<int, int>::func() { printf("Special\n"); }
しかし,テンプレート引数Bの汎用性が失われるので,これは嬉しくない.
方法3 (エラーとならないが,他の関数が冗長になってしまう.)
テンプレートクラス自体を特殊化して再定義してみる.
template<typename B> class XXXX<int, B> { public: void func() { printf("Special\n"); } int func2() { return 100; } };
これはコンパイルに通る.しかし,特殊化しなくてもよかった関数 func2() が冗長になってしまっている.
やはり嬉しくない.
方法4 (正解)
で,たぶんこれが正解.
特殊化したい関数(ここでは func() ) のみから成る,テンプレート関数オブジェクトを作り,そいつを特殊化する.
template<typename A, typename B> class XXXX { private: template<typename C, typename D> struct F { static void func() { printf("General\n"); } }; template<typename D> struct F<int, D> { static void func() { printf("Special\n"); } }; public: void func() { F<A, B>::func(); } int func2() { return 100; } };
boost::mpl とか使ったやり方もいろいろあると思われますが,ひとまず目的は達成できました.
方法4++
関数オブジェクトはええけど,そうしたらもとのクラスの変数に触れなくなるやん?という
ことがありえます.
1つ2つなら,その変数を引数に渡せばいいし,いろいろ触るんやったら外側クラスの
ポインタや参照を関数オブジェクトの関数に渡すのがよいかと思います.
ポインタや参照を渡す場合で,親クラスの変数がprivateだったりする場合は,
外側クラスのfriendとして,関数オブジェクトを定義するのがよかったりするかも知れません.
friendは悪手とする方もいますけど,そこはお好みで.
template<typename A, typename B> class Temp { template<typename C, typename D> friend struct Func; private: template<typename C, typename D> struct Func { static void Hello(const Temp& temp) { printf("General: size=%lu, size=%lu x=%d\n", sizeof(C), sizeof(D), temp.x); } }; template<typename C> struct Func<C, int> { static void Hello(const Temp& temp) { printf("Special: size=%lu, size=%lu x=%d\n", sizeof(C), sizeof(int), temp.x); } }; int x; public: void Hello() { x=100; Func<A, B>::Hello(*this); } };
他にいいやり方があったら教えてくらさい.