メンバ関数の部分特殊化がしたい
C++ でメンバ関数を部分特殊化しようとすると(例えばテンプレートクラスのテンプレートメンバ関数を,ある特定の型について特殊化する),コンパイルエラーとなる.gcc なら "error: invalid use of incomplete type" だとか, "error: enclosing class templates are not explicitly specialized" などのエラーが発生する.しかし,現実には,部分特殊化したいことも多い.そこで,やや強引な解決策を実装してみた.
問題提起
template <typename T1, typename T2> struct TTest { T1 ex() { ... } }; template <> void TTest<void, void>::ex() // OK { ... }
これは,メンバ関数 TTest::ex を T1=void, T2=void について(完全に)特殊化している例で,ちゃんとコンパイルできる.これに対し,
template <typename T1, typename T2> struct TTest { T1 ex() { ... } }; template <typename T2> void TTest<void, T2>::ex() // NG { ... }
このように,メンバ関数 TTest::ex を T1=void のみについて部分特殊化すると(i.e. T2 についてはテンプレートのまま),コンパイルエラーとなる. gcc の場合, "error: invalid use of incomplete type 'struct TTest
解決
クラス TTest 内にテンプレート関数オブジェクトを作る.部分特殊化したい関数の代わりに,このテンプレート関数オブジェクトを特殊化しておき,部分特殊化したい関数からは,このテンプレート関数オブジェクトを呼び出す.ただし,テンプレート関数オブジェクトの完全な特殊化を C++ は許していないから,ダミーテンプレート引数を設けることで対処する.以上をまとめると,
template <typename T1, typename T2> struct TTest { T1 ex() { // 特殊化したい関数からは,関数オブジェクト dummy_ex を呼び出す return dummy_ex<T1>()(); } // 通常の型 T1 に対する実装: template <typename TX, typename dummy_type=void> struct dummy_ex { TX operator()() { ... } }; // dummy_ex を void について特殊化し, T1==void に対して実装する: template <typename TX> struct dummy_ex <void, TX> { void operator()() { ... } }; };
ダミーテンプレート引数 dummy_type を持たせない場合, dummy_ex の特殊化は完全な特殊化になってしまい,"error: explicit specialization in non-namespace scope 'struct TTest
クラス外部で実装する場合は,
template <typename T1, typename T2> struct TTest { T1 ex() { return dummy_ex<T1>()(); } template <typename TX, typename dummy_type=void> struct dummy_ex; }; // 通常の型 T1 に対する実装: template <typename T1, typename T2> template <typename TX, typename dummy_type> struct TTest<T1,T2>::dummy_ex { TX operator()() { ... } }; // dummy_ex を void について特殊化し, T1==void に対して実装する: template <typename T1, typename T2> template <typename TX> struct TTest<T1,T2>::dummy_ex< void, TX > { void operator()() { ... } };
のように書けばよい.この場合も, dummy_type を除去すると,"error: enclosing class templates are not explicitly specialized" というエラーが発生し,コンパイルできない.
少し脱線: 参照型,ポインタ型への特殊化
dummy_ex を TX& とか TX* に特殊化する場合,部分特殊化となるため dummy_type は不要となる.例えばポインタ型への特殊化は,
template <typename T1, typename T2> struct TTest { T1 ex() { return dummy_ex<T1>()(); } template <typename TX> struct dummy_ex { TX operator()() { ... } }; // ポインタ型に対する部分特殊化 template <typename TX> struct dummy_ex<TX*> { TX* operator()() { ... } }; };
のような感じになる.
サンプル
簡単なサンプルコードをひとつ.
#include <iostream> using namespace std; template <typename T1, typename T2> struct TTest { T1 ex() { return dummy_ex<T1>()(); } template <typename TX, typename dummy_type=void> struct dummy_ex { TX operator()() { TX tmp(1); cout<<"default member function"<<endl; return tmp; } }; template <typename TX> struct dummy_ex< void, TX > { void operator()() { cout<<"member function specialized to void"<<endl; return; } }; }; int main(int argc, char**argv) { TTest<int,int> test1; TTest<void,int> test2; test1.ex(); test2.ex(); return 0; }