2016-11-28 3 views
1

ここから(endo)ファンクタは、オブジェクトを取得して同じタイプの別のオブジェクトで変換できるものです。ファンクタの最も単純な例は、IDです。endofunctorのコンセプトまたはインターフェイス

struct Identity { 
    template <typename T> 
    T Apply(T x) { 
     return x 
    } 
}; 

一般的なFunctorを識別する「Functorタイプ」が必要です。私は何をしたいのようなコードを記述している:

class Sum { 
    public: 
    Sum(Functor* f, Functor* g) : 
     f_(f), 
     g_(g) {} 
    template <typename T> 
    T Apply(T x) { return f_->Apply(x) + g_->Apply(x); } 
    private 
    Functor* f_; 
    Functor* g_; 
}; 

私の心に来た最初のアイデアは、仮想クラスを使用して、もちろんです:

struct Functor { 
    template <typename T> 
    virtual T Apply(T x) = 0; 
}; 

このアプローチの解決不可能な問題は、テンプレートということですバーチャルにすることはできません。

次に、私はC++の概念を使ってみました。しかし、Specifying a concept for a type that has a member function template using Concepts LiteC++ Concepts: Can I define a concept that is itself a template?に記載されているように、「テンプレート化された概念」を持つことはできません。

最後に、私はHow to achieve "virtual template function" in C++につまずいてきたので、私は、次の可能な実装を思い付いた:

struct Functor { 
    template <typename T> 
    T Apply(const T& x); // No more virtual!!! 
}; 

// ... Identity and Sum declarations properly inheriting from Functor ... 

template <typename T> 
T Functor::Apply(T x) { 
    if (Identity* specialized = 
      dynamic_cast<Identity*>(this)) { 
     return specialized->Apply(x); 
    } else if (const Sum* specialized = 
      dynamic_cast<const Sum*>(this)) { 
     return specialized->Apply(x); 
    } else ... 
} 

これがコンパイルされていても、それが最善の解決策ではありません。主な問題は、パフォーマンスとコードの繰り返しです。 パフォーマンスの問題は、FunctorでApplyが呼び出されるたびに、Functor :: Apply内の長いif節が解決されなければならないという事実に由来します。これは大きな問題です.Functorを深く入れ子にすることができます(Applyを呼び出すとFunctor :: Applyに複数の呼び出しが行われる可能性があります)。新しいFunctorを定義するたびに、Functor :: Modifyを変更して新しいif節を追加する必要があるため、「コードの繰り返し」の問題は非常に自明です。

私がここで求めているのは、Sumのようなクラスを作成できるFunctorインターフェイス/コンセプトを定義する適切な(クリーンな)方法があるかどうかです。 C++の概念と重いテンプレートのメタプログラミングが受け入れられます。

p.s.すべてのコードスニペットは、目的に応じてできるだけシンプルに保たれています。構造体の代わりにクラスを使用することや、const識別子を追加することや一意のポインタを使用することを避けることは避けてください。

+1

私はstd :: functionをファンクタとして使用しようとします。 –

+2

"C++ 1z"には概念がありません。 –

+0

タイプ「T」がすべての関数で同じ場合は、仮想関数を使用する場合は、関数レベルではなくクラスレベルで移動することができます。ただし、最適な解決策ではない可能性があります。すでに提案したように、私は 'std :: function'が役立つかどうかを調べることから始めます。 – Phil1970

答えて

2

残念なことに私が考えることができる(最良の)ソリューションのほとんどは、かなり複雑な方法論を採用する必要があります。もちろん、それは必ずしも悪いことではありませんが、プログラムの設計に進むと混乱を招く可能性があります。

template <typename F, typename G> 
class Sum { 
    public:   
    Sum(F& f, G& g) : 
     f_(f), 
     g_(g) {} 
    template <typename T> 
    inline T Apply(T x) { return f_.Apply(x) + g_.Apply(x); } 
    private: 
    F& f_; 
    G& g_; 
}; 

/* 
    For every class like the above, you may want to define an 
    easy-to-use generating function to simplify instantiations: 
*/ 
template <typename F, typename G> 
inline Sum<F, G> MakeSum(F& f, G& g) 
{ 
    return Sum<F, G>(f, g); 
} 

#include <cmath> 

struct SquareRoot { 
    template <typename T> 
    inline T Apply(T x) 
    { 
     return std::sqrt(x); 
    } 
}; 

struct Triple { 
    template <typename T> 
    inline T Apply(T x) 
    { 
     return T(3) * x; 
    } 
}; 

// Example: 

#include <iostream> 

int main(void) 
{ 
    using namespace std; 
    SquareRoot square_root; 
    Triple triple; 
    // For g++, don't forget to compile with -std=c++1z  
    auto sum = MakeSum(square_root, triple); 
    cout << sum.Apply(1024) << endl; 
} 

が、それは他の技術のように強力ではありませんが、確かに、それはそれにもかかわらず、良い出発点であってもよい。そのために、私はおそらくストレートフォワード、もう少し何かをお勧めしたいです。

+0

ありがとうございました。また、「かなり複雑な方法論」の1つを示すことができますか? – dario2994

+0

私はおそらく大まかな例を投稿することができますが、実際には数百行のコードを必要とすることがあります。このようなテクニックを使って細かいことについて私の2番目の答え(下記)を見てください。 –

+0

申し訳ありませんが、私の2番目の例は未定義の動作に依存しています。私はそれを修正し、後で返信しようとします... –

関連する問題