2010-05-15 113 views
9

有許多令人印象深刻的Boost庫,如Boost.Lambda或Boost.Phoenix,它們將C++變成真正的函數式語言有很大的幫助。但是有沒有一種直接的方法可以從任意兩個或更多的任意函數或函子創建複合函數?C++中的函數組合

如果我有:int f(int x)int g(int x),我要像做f . g這將靜態生成一個新的函數對象等同於f(g(x)).

這似乎是通過各種技術,如討論here成爲可能。當然,您可以將呼叫鏈接到boost::lambda::bind以創建複合函子。但是,有沒有在升壓任何事情很容易讓你採取任何2個或多個函數或函數對象,並結合他們創造一個單一的複合仿函數,類似於你會怎麼做它象Haskell語言?

+1

你可以用'提振:: bind',而不是'提高::拉姆達::綁定「這個任務。它看起來像'bind(g,bind(f,_1))'。你知道嗎? – 2010-05-15 20:49:26

+0

是的,您可以使用嵌套調用來'boost :: bind'來創建複合函子。但是,我想知道是否有更好的方法來做到這一點。 – Channel72 2010-05-15 20:57:55

回答

3

我不知道任何支持當前語法的東西。但是,創建一個會很簡單。簡單地重寫爲*函子(升壓::功能<>例如),使得它返回複合算符。


template < typename R1, typename R2, typename T1, typename T2 > 
boost::function<R1(T2)> operator * (boost::function<R1(T2)> const& f, boost::function<R2(T2)> const& g) 
{ 
    return boost::bind(f, boost::bind(g, _1)); 
} 

未經測試,但我懷疑它是關閉,如果它不開箱即用。

+1

因爲原始函數既不是類也不是枚舉,所以像'f1 * f2'這樣的用法不會尋找重載操作符,儘管:(因爲它不能乘以兩個函數,所以會使用內建運算符並失敗 – 2010-05-15 23:41:12

+0

這就是有作爲二等公民... – 2010-05-16 12:34:38

+0

是的,只適用於仿函數,你可以創建一個實用函數,將其轉換爲boost ::函數來匹配常規函數 – 2010-05-16 19:21:54

1

模板他們。

template<typename T1> class FunctorOne { 
    FunctorOne(T1 newt) 
     : t(newt) {} 
    void operator()() { 
     t(); 
    } 
    T1 t; 
}; 
template<> class FunctorOne<void> { 
    void operator()() { 
    } 
}; 
template<typename T1> class FunctorTwo { 
    FunctorOne(T1 newt) 
     : t(newt) {} 
    void operator()() { 
     t(); 
    } 
    T1 t; 
}; 
template<> class FunctorTwo<void> { 
    void operator()() { 
    } 
}; 
FunctorOne<FunctorTwo<FunctorOne<FunctorTwo<void>>>>> strangefunctionobject(FunctorTwo(FunctorOne(FunctorTwo())); 

推薦使用typedefs。
編輯:哎呦。原來,構造函數中的類型推斷很糟糕。我會在一分鐘之內回覆一些實際工作的內容:P
更多編輯:
如果您只需要函數而不是函數,您可以創建一個新實例,甚至只需使用靜態函數。

template<typename T1, typename T2> class FunctorOne { 
public: 
    static bool Call() { 
     T1::Call(T2::Call()); 
     return true; 
    } 
}; 
template<> class FunctorOne<void, void> { 
public: 
    static bool Call() { 
    } 
}; 
template<typename T1> class FunctorTwo { 
public: 
    static bool Call() { 
     T1::Call(); 
    } 
}; 
template<> class FunctorTwo<void> { 
public: 
    static bool Call() { 
    } 
}; 

bool haicakes = FunctorOne<FunctorTwo<void>, FunctorTwo<void>>::Call(); 

這假定在任何給定的函數中,您可以手動處理每個不同的簽名。使用decltype可以在這方面用C++ 0x編譯器。

4

在這個問題上磕磕絆絆,我想指出今天遇到這個問題的任何人,只要使用標準庫和一些輔助類,就可以使用相對優雅的語法,這要歸功於decltype,auto和perfect轉發。

定義這兩個類:

template <class Arg, class ArgCall, class OuterCall> 
class pipe { 
private: 
    ArgCall argcall; 
    OuterCall outercall; 
public: 
    typedef pipe<Arg, ArgCall, OuterCall> this_type; 
    pipe(ArgCall ac, OuterCall oc) : argcall(ac), outercall(oc) {} 
    auto operator()(Arg arg) -> decltype(outercall(argcall(arg))) { 
     return outercall(argcall(arg)); 
    } 
    template <class NewCall> 
    pipe<Arg, this_type, NewCall> operator[](NewCall&& nc) { 
     return {*this, std::forward<NewCall>(nc)}; 
    } 
}; 

template <class Arg> 
class pipe_source { 
public: 
    typedef pipe_source<Arg> this_type; 
    Arg operator()(Arg arg) { 
     return arg; 
    } 
    template <class ArgCall, class OuterCall> 
    static pipe<Arg, ArgCall, OuterCall> create(ArgCall&& ac, OuterCall&& oc) { 
     return {std::forward<ArgCall>(ac), std::forward<OuterCall>(oc)}; 
    } 
    template <class OuterCall> 
    pipe<Arg, this_type, OuterCall> operator[](OuterCall&& oc) { 
     return {*this, std::forward<OuterCall>(oc)}; 
    } 
}; 

一個簡單的程序:

int f(int x) { 
     return x*x; 
} 

int g(int x) { 
     return x-2; 
} 

int h(int x) { 
     return x/2; 
} 

int main() { 
     auto foo = pipe_source<int>::create(f, g); 
     //or: 
     auto bar = pipe_source<int>()[g][h]; 
     std::cout << foo(10) << std::endl; 
     std::cout << bar(10) << std::endl; 
     return 0; 
} 

這有額外的好處,一旦它在管道,只要返回類型是正確的,你可以添加另一個功能f連接到管道[f]。

然後:

$ g++ test.cpp -o test -std=c++11 
$ ./test 
98 
4 
$ 
0

C++ 11。沒有提升。沒有助手類。任何數量的參數。只需std :: function和variadic模板。

template <typename F1, typename F2> 
struct function_composition_traits : public function_composition_traits<decltype(&F1::operator()), decltype(&F2::operator())> 
{}; 

template <typename ClassType1, typename ReturnType1, typename... Args1, typename ClassType2, typename ReturnType2, typename... Args2> 
struct function_composition_traits<ReturnType1(ClassType1::*)(Args1...) const, ReturnType2(ClassType2::*)(Args2...) const> 
{ 
     typedef std::function<ReturnType2(Args1...)> composition; 

     template <typename Func1, typename Func2> 
     inline static composition compose(const Func1& f1, const Func2& f2) { 
      return [f1,f2](Args1... args) -> ReturnType2 { return f2(f1(std::forward<Args1>(args)...)); }; 
     } 
}; 

template <typename F1, typename F2> 
typename function_composition_traits<F1,F2>::composition compose(const F1& lambda1,const F2& lambda2) 
{ 
     return function_composition_traits<F1,F2>::template compose<F1,F2>(lambda1, lambda2); 
} 

template <typename F, typename... Fs> 
auto compose(F f, Fs... fs) -> decltype(compose(f, compose(fs...))) 
{ 
     return compose(f, compose(std::forward<Fs>(fs)...)); 
} 

用法:

auto add = [](int x, int y){ return x+y; }; 
auto mul = [](int x){ return x*2; }; 
auto divide = [](int x) { return (double)x/3.0; }; 
auto test = compose(add, mul, divide); 

cout << "Result: " << test(2,3); 

輸出:

結果:3.33333