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<void, T2>'" というエラーだ.
解決 †
クラス 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<T1, T2>'" などというエラーを吐く.
クラス外部で実装する場合は,
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;
}
g++ に "-ansi -pedantic -Wall" (厳格なANSI準拠モード)などをつけてコンパイルした.
関連サイト †
- Explicit specialization in non-namespace scope : c++
- enclosing class templates are not explicitly specialized? - CodeGuru Forums
- 突然クラスのテンプレートメンバー関数の特殊化ができるか気になった
- 続)メンバー関数テンプレートの特殊化
コンパイラによっては,エラーのでかたにかなり違いがあるらしい.間違い・コンパイラ依存性などを教えて頂けると助かります.