2016-11-28 8 views
1

여기에서 (endo) functor는 객체를 가져 와서 같은 유형의 다른 객체에서 변형 할 수있는 것입니다. 가장 간단한 functor의 예는 ID입니다.endofunctor 개념 또는 인터페이스

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

일반 Functor를 식별하는 "Functor type"이 필요합니다.

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

이 접근 방식의 해결 못하는 문제가 서식한다는 것입니다 : 내 마음에 온

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_; 
}; 

첫 번째 아이디어 물론 가상 클래스를 사용한다 : 내가 뭘하고 싶은처럼 쓰기 코드는 가상 일 수는 없다.

그런 다음 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 절을 추가 할 때마다 분명합니다.

여기서 내가 묻는 것은 합계와 같은 클래스를 만들 수있는 Functor 인터페이스/개념을 정의하는 적절한 (더 명확한) 방법이 있는지 여부입니다. C++ 개념 및 무거운 템플릿 메타 프로그래밍이 허용됩니다.

p.s. 모든 코드 스 니펫은 가능한 한 간단하게 유지됩니다. 구조체 대신 클래스를 사용하거나 const 식별자를 추가하거나 고유 한 포인터를 사용하라는 제안은 피하십시오.이 질문의 요지는 아닙니다.

+1

저는 std :: function을 펑터 (functor)로 사용하려고합니다. –

+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

답변 해 주셔서 감사합니다. 또한 "상당히 복잡한 방법론"중 하나를 보여줄 수 있습니까? – dario2994

+0

대략적인 예제를 게시 할 수는 있지만 실제로 수백 줄의 코드가 필요할 수있는 본격적인 솔루션을 함께 사용하는 것은 실제로 불가능합니다. 그러한 기술을 사용하여 정교함을 얻으려면 두 번째 대답 (아래)을 참조하십시오. –

+0

미안하지만 두 번째 예제는 정의되지 않은 동작에 의존합니다. 나는 그것을 고치고 나중에 다시 게시하려고 노력할 것입니다 ... –