2017-03-05 9 views
1

テンプレートパラメータの値に基づいて関数内の文を条件付きでコンパイルできますか?例:(!Tが使用その後、すべてのタイプが定義された=演算子を持っている必要があるため)C++テンプレートパラメータ値に基づいてメンバ関数の定義を変更する

template<typename T, bool chk> 
class subject 
{ 
public: 
    // the ideal case  
    void doSomething(T new_val) 
    { 
     if(chk) 
     { 
      if(new_val != val) 
       //do_something only if new_val is different from val 
     } 
     else 
     { 
      //do_something even if new_val and val are equal 
     } 
    } 


    //or if that's not possible, if chk = 0 use this method 
    void doSomething(T new_val) 
    { 
     //do_something even if new_val and val are equal 
    } 

    // and if chk = 1 use this method 
    void doSomething(T new_val) 
    { 
     if(new_val != val) 
      //do_something only if new_val is different from val 
    } 

    T val; 
}; 

キャッチがCHKの値に基づいており、私も機能にコンパイル声明if(new_val!=val)を望んでいません。

私は、このアプローチの欠点の一つは、CHKが0または1(たとえばwatch(foo<int>))であれば気にしない関数を定義することはできないのでfoo<int,0>foo<int,1>が異なるクラスであるということですね。

私が特に見ているアプリケーションはオブザーバであり、一部のタイプでは、値が実際に変更された場合のみオブザーバに通知し、オブザーバには常に通知する必要があります。 !=演算子を定義する必要はありません)。

これは2つのクラスを別に持つことなく可能ですか?

答えて

2

これは2つの別々のクラスを持たなくても可能ですか?

はい、そうです。あなたは、コードの重複を避けるために、あなたのクラスを専門にしたくない場合は、次の例のいずれかのようsfinae式を使用することができます。

#include <type_traits> 
#include <iostream> 

template<typename T, bool chk> 
struct subject { 
    template<bool trigger = chk> 
    std::enable_if_t<trigger> 
    doSomething(T new_val) { 
     if(new_val != val) { 
      std::cout << "new_val != val" << std::endl; 
     } else { 
      std::cout << "new_val == val" << std::endl; 
     } 
    } 

    template<bool trigger = chk> 
    std::enable_if_t<not trigger> 
    doSomething(T new_val) { 
     std::cout << "who cares?" << std::endl; 
    } 

    T val; 
}; 

int main() { 
    subject<int, true> s1{0}; 
    s1.doSomething(0); 
    s1.doSomething(1); 
    subject<int, false> s2{0}; 
    s2.doSomething(0); 
    s2.doSomething(1); 
} 

考え方は、定義doSomethingのためのコンパイル時にピックアップされ、テンプレートパラメータchkの値に依存します。他の定義は単に期待どおりに破棄され、まったく使用できなくなります。
sfinae式が機能するには、triggerテンプレートパラメータが、メンバ関数テンプレートの実際のパラメータでなければならないことに注意してください。

template<bool trigger = chk> 
sfinae_expression_based_on_enable_if 
doSomething(T new_val) { /* ... */ } 

coliru上で実行されている上、それを参照してください:あなたはそれをこのように定義する必要が理由です。

+0

これは素晴らしいことです。私はenable_ifを見ていましたが、IMOではcppreference.com上のドキュメントは理解できず、これにはあまり適していないように見えました(SFINAEの仕組みが分かりません) –

+0

実際にはあなたはenable_if_tがあなたにちょうどenable_ifを超えて与えるものとそれがどのように働くかの簡単な説明を追加するのに気をつけますか?もう一度私は見てきましたが、少し不足しています。 –

+0

@schrödinbugここではたくさんの例が見つかります。私ができることよりも良く説明されました! ;-) – skypjack

0

あなたが探しているのは、「テンプレートの特殊化」です。

テンプレートを特殊化する必要があります。お使いのベースのテンプレートを定義した後、上記のように、あなたが進み、その特殊化を定義します:

template<typename T> 
class subject<T, true> 
{ 
public: 

    // ... 

あなたは2番目のテンプレートパラメータの場合のために、あなたが必要な変更を行うこと、最初から全体subjectクラスを進めると定義true(またはfalse、それがあなたが専門にする必要がある場合は)です。物事を取り除いたり、物事を追加したり、物事を完全に変更したりすることができます。特殊クラスは、異なるクラスメンバー、メソッド、または同じクラスメソッドを持つことができますが、完全に異なった働きをします。

異なるビットだけでなく、クラス全体を定義していることを理解することが重要です。クラスのわずかな部分だけを本当に特殊化する必要がある場合は、もちろん、重複したコードが生成されます。それをリファクタリングしたり、変化するビットをヘルパークラスや関数に入れたり、変化するビットだけを特殊化することがしばしば必要になります。

今後のC++ 17標準には、テンプレートの特殊化の代替手段がいくつかあります。しかし、専門化は伝統的に、これらの種類の状況のた​​めに学ぶ最初のものです。したがって、C++の本を、テンプレートの特殊化について説明している章に移動して、まずC++の新しいものを鍛える前に、ベルトの下を手に入れてください。

+0

私は特殊化を見ていましたが、あなたはそれが大量の複製を必要とし、一般的に(すなわち、chkが真か偽であっても)議論対象として機能することはできないと思います。 )私はあなたが関数をオーバーロードする必要があると思います。うーん。 C++ 17のどの機能を参照していますか? –

+0

2番目の段落の最後の半分を読み返すべきです。限り、C + + 17行く、私は[constexpr if](http://en.cppreference.com/w/cpp/language/if#Constexpr_If)を参照していた。 –

関連する問題