6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

テンプレートクラスのメソッドの部分特殊化

Last updated at Posted at 2016-07-01

やりたいこと

// ※このコードはコンパイルできません
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を呼んでくれる、なんてことはないんですね・・・
だから、部分特殊化で①の場合、全部コピーしないといけない、のかな?

6
4
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?