やりたいこと
// ※このコードはコンパイルできません
template<class A, bool b = false>
class Outer {
public:
void func(void) { cout << "Default" <<endl;};
}
// 第二テンプレート引数をtrueにすると、メソッドの挙動が変わるようにしたい
template <class A>
void Outer<A, true>::func(void) { cout << "Special" <<endl;};
回答
boolの値を一旦型にマッピングし、関数のオーバーロードでどうにかする
template <class A, bool b = false>
class Outer2 {
private:
// "maps" a bool value to a struct type
template<bool B> struct i2t {};
void funcImpl(i2t<true>) { cout << "Special" <<endl;};
void funcImpl(i2t<false>) { cout << "Default" <<endl;};
public:
inline void func() { funcImpl(i2t<b>()); }
}
コメント欄から追記
最適化に任せる
単純にこれで。コンパイラがなんとかしてくれます。
template<class A, bool b = false>
class Outer {
public:
void func(void)
{
if (b) {
cout << "Special" << endl;
} else {
cout << "Default" << endl;
}
}
};
この程度の例だと起こりませんが、たとえ最適化で落とされる方でもコードがコンパイルエラーになるような場合は、上手く行かない場合もあります。
(akinomyogaさんのコメント参照)
SFINAEを使う
template< class A, bool b = false >
class Outer {
public:
template< bool b0 = b, std::enable_if_t< b0, std::nullptr_t > = nullptr >
void func()
{ std::cout << "true" << std::endl;};
template< bool b0 = b, std::enable_if_t< !b0, std::nullptr_t > = nullptr >
void func()
{ std::cout << "false" << std::endl;};
};
他に試したこと。
私が一番かっちょよくて、記述のコストも少ないなと思ったのは上の方法ですが、
他の方法として
①クラス全体を部分特殊化する
②部分特殊化したい関数を子クラスや関数オブジェクトに移譲する
という方法もあるみたいです
①の方法は、小さなクラスなら良いですが、大きなクラスだと大量にコピーする必要があり大変そうだと思いました。今回のようにスイッチひとつぐらいならともかく、intで渡した値で特殊化するとか、複数スイッチがあるような場合は管理コストも大変そうかなと。
もしくはテンプレートパラメータで切り替えられる一連のメソッド郡をまとめたクラスを作り、interface classみたいにして使うとか?
②の方法は、構造が階層化されてしまうため、親クラスのメンバやメソットを触りたい場合は、thisを渡してそれ経由で触る必要があります。これはちょっとわずらわしいかなと。
// ②の例
template<class A, bool b = false>
class Outer3 {
public:
// inner class の primary な定義
template<class iA, bool ib>
struct Inner { void func() { cout << "Default" <<endl;} };
// inner class をtrueの場合に特殊化
template<class iA>
struct Inner<iA, true> { void func() { cout << "Special" << endl;} };
// 移譲
inline void func() { Inner<A, b>::func(); }
}
また部分特殊化でなければ、また話は変わってくるとおもいます
Aがなければ、これでよい
// これは可
template<bool b = false>
class Outer4 {
public:
void func(void) { cout << "Default" <<endl;};
}
// Outer全体を特殊化
template <>
void Outer4<true>::func(void) { cout << "Special" <<endl;};
参考文献
ここの方法を参考にしました
より詳しくはこちらをご覧ください
http://stackoverflow.com/questions/1501357/template-specialization-of-particular-members
どのテンプレートが特殊化・部分特殊化できる・できないか
http://mimosa-pudica.net/cpp-specialization.html
partial template specialization
http://en.cppreference.com/w/cpp/language/partial_specialization
Members of partial specializations
の項目のa2.f()
の呼び出しがエラーになる
自動的にprimary テンプレートのf
を呼んでくれる、なんてことはないんですね・・・
だから、部分特殊化で①の場合、全部コピーしないといけない、のかな?