如何在C++ 0x中重载运算符以组合函数?

sin*_*raj 6 lambda functional-programming composition c++11

有没有办法超载,比如>>操作员的功能组合?操作员应该在lambda上无缝地工作std::function

要求:

  • 解决方案不应包含嵌套bind调用,
  • 左操作数可以是具有任意数量参数的函数类型,和
  • 不应创建多个函数对象实例.

这是一个快速而肮脏的示例,说明了所需的行为:

#include <iostream>
#include <functional>

using namespace std;

// An example of a quick and dirty function composition.
// Note that instead of 'std::function' this operator should accept
// any functional/callable type (just like 'bind').
template<typename R1, typename R2, typename... ArgTypes1>
function<R2(ArgTypes1...)> operator >> (
                const function<R1(ArgTypes1...)>& f1,
                const function<R2(R1)>& f2) {
    return [=](ArgTypes1... args){ return f2(f1(args...)); };
}

int main(int argc, char **args) {
    auto l1 = [](int i, int j) {return i + j;};
    auto l2 = [](int i) {return i * i;};

    function<int(int, int)> f1 = l1;
    function<int(int)> f2 = l2;

    cout << "Function composition: " << (f1 >> f2)(3, 5) << endl;

    // The following is desired, but it doesn't compile as it is:
    cout << "Function composition: " << (l1 >> l2)(3, 5) << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

GMa*_*ckG 3

(l1 >> l2)永远无法工作。

它们是由编译器创建的函数对象,不包含该运算符,因此除非您计划将编译器修改为不合格,否则它总是会这样。:)

但是,您可以引入一个“关键字”(实用程序类),这可以说是一件好事,但它很重:

// https://ideone.com/MS2E3

#include <iostream>
#include <functional>

namespace detail
{
    template <typename R, typename... Args>
    class composed_function;

    // utility stuff
    template <typename... Args>
    struct variadic_typedef;

    template <typename Func>
    struct callable_type_info :
        callable_type_info<decltype(&Func::operator())>
    {};

    template <typename Func>
    struct callable_type_info<Func*> :
        callable_type_info<Func>
    {};

    template <typename DeducedR, typename... DeducedArgs>
    struct callable_type_info<DeducedR(DeducedArgs...)>
    {
        typedef DeducedR return_type;
        typedef variadic_typedef<DeducedArgs...> args_type;
    };

    template <typename O, typename DeducedR, typename... DeducedArgs>
    struct callable_type_info<DeducedR (O::*)(DeducedArgs...) const>
    {
        typedef DeducedR return_type;
        typedef variadic_typedef<DeducedArgs...> args_type;
    };

    template <typename DeducedR, typename... DeducedArgs>
    struct callable_type_info<std::function<DeducedR(DeducedArgs...)>>
    {
        typedef DeducedR return_type;
        typedef variadic_typedef<DeducedArgs...> args_type;
    };

    template <typename Func>
    struct return_type
    {
        typedef typename callable_type_info<Func>::return_type type;
    };

    template <typename Func>
    struct args_type
    {
        typedef typename callable_type_info<Func>::args_type type;
    };

    template <typename FuncR, typename... FuncArgs>
    struct composed_function_type
    {
        typedef composed_function<FuncR, FuncArgs...> type;
    };

    template <typename FuncR, typename... FuncArgs>
    struct composed_function_type<FuncR, variadic_typedef<FuncArgs...>> :
        composed_function_type<FuncR, FuncArgs...>
    {};

    template <typename R, typename... Args>
    class composed_function
    {
    public:
        composed_function(std::function<R(Args...)> func) :
        mFunction(std::move(func))
        {}

        template <typename... CallArgs>
        R operator()(CallArgs&&... args)
        {
            return mFunction(std::forward<CallArgs>(args)...);
        }

        template <typename Func>
        typename composed_function_type<
                    typename return_type<Func>::type, Args...>::type
             operator>>(Func func) /* && */ // rvalues only (unsupported for now)
        {
            std::function<R(Args...)> thisFunc = std::move(mFunction);

            return typename composed_function_type<
                                typename return_type<Func>::type, Args...>::type(
                                        [=](Args... args)
                                        {
                                            return func(thisFunc(args...));
                                        });
        }

    private:    
        std::function<R(Args...)> mFunction;
    };
}

template <typename Func>
typename detail::composed_function_type<
            typename detail::return_type<Func>::type,
                typename detail::args_type<Func>::type>::type
    compose(Func func)
{
    return typename detail::composed_function_type<
                        typename detail::return_type<Func>::type,
                            typename detail::args_type<Func>::type>::type(func);
}

int main()
{
    using namespace std;

    auto l1 = [](int i, int j) {return i + j;};
    auto l2 = [](int i) {return i * i;};

    std:function<int(int, int)> f1 = l1;
    function<int(int)> f2 = l2;

    cout << "Function composition: " << (compose(f1) >> f2)(3, 5) << endl;
    cout << "Function composition: " << (compose(l1) >> l2)(3, 5) << endl;
    cout << "Function composition: " << (compose(f1) >> l2)(3, 5) << endl;
    cout << "Function composition: " << (compose(l1) >> f2)(3, 5) << endl;

    return 0;
Run Code Online (Sandbox Code Playgroud)

这是相当多的代码!不幸的是我不知道如何减少它。

你可以走另一条路,只是为了在你的方案中使用 lambda,你只需要显式地将它们设置为std::function<>s,但它不太统一。上面的一些机制可以用来创建某种to_function()函数,将 lambda 函数转换为std::function<>s。