如何在 C++ 中进行柯里化?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/152005/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:16:45  来源:igfitidea点击:

How can currying be done in C++?

c++stlfunctional-programmingcurryingbinders

提问by yesraaj

What is currying?

什么是咖喱?

How can currying be done in C++?

如何在 C++ 中进行柯里化?

Please Explain binders in STL container?

请解释 STL 容器中的粘合剂?

采纳答案by Greg Hewgill

In short, currying takes a function f(x, y)and given a fixed Y, gives a new function g(x)where

简而言之,currying 接受一个函数f(x, y)并给定一个 fixed Y,给出一个新函数g(x),其中

g(x) == f(x, Y)

This new function may be called in situations where only one argument is supplied, and passes the call on to the original ffunction with the fixed Yargument.

这个新函数可以在只提供一个参数的情况下被调用,并将调用传递给f带有固定Y参数的原始函数。

The binders in the STL allow you to do this for C++ functions. For example:

STL 中的绑定器允许您为 C++ 函数执行此操作。例如:

#include <functional>
#include <iostream>
#include <vector>

using namespace std;

// declare a binary function object
class adder: public binary_function<int, int, int> {
public:
    int operator()(int x, int y) const
    {
        return x + y;
    }
};

int main()
{
    // initialise some sample data
    vector<int> a, b;
    a.push_back(1);
    a.push_back(2);
    a.push_back(3);

    // here we declare a function object f and try it out
    adder f;
    cout << "f(2, 3) = " << f(2, 3) << endl;

    // transform() expects a function with one argument, so we use
    // bind2nd to make a new function based on f, that takes one
    // argument and adds 5 to it
    transform(a.begin(), a.end(), back_inserter(b), bind2nd(f, 5));

    // output b to see what we got
    cout << "b = [" << endl;
    for (vector<int>::iterator i = b.begin(); i != b.end(); ++i) {
        cout << "  " << *i << endl;
    }
    cout << "]" << endl;

    return 0;
}

回答by Julian

1. What is currying?

1.什么是柯里化?

Currying simply means a transformation of a function of several arguments to a function of a single argument. This is most easily illustrated using an example:

柯里化只是意味着将具有多个参数的函数转换为具有单个参数的函数。使用示例最容易说明这一点:

Take a function fthat accepts three arguments:

取一个f接受三个参数的函数:

int
f(int a,std::string b,float c)
{
    // do something with a, b, and c
    return 0;
}

If we want to call f, we have to provide all of its arguments f(1,"some string",19.7f).

如果我们想调用f,我们必须提供它的所有参数f(1,"some string",19.7f)

Then a curried version of f, let's call it curried_f=curry(f)only expects a single argument, that corresponds to the first argument of f, namely the argument a. Additionally, f(1,"some string",19.7f)can also be written using the curried version as curried_f(1)("some string")(19.7f). The return value of curried_f(1)on the other hand is just another function, that handles the next argument of f. In the end, we end up with a function or callable curried_fthat fulfills the following equality:

然后是 的柯里化版本f,我们称之为curried_f=curry(f)只需要一个参数,它对应于 的第一个参数f,即参数a。此外,f(1,"some string",19.7f)也可以使用柯里化版本编写为curried_f(1)("some string")(19.7f). curried_f(1)另一方面,的返回值只是另一个函数,它处理 的下一个参数f。最后,我们得到一个curried_f满足以下等式的函数或可调用对象:

curried_f(first_arg)(second_arg)...(last_arg) == f(first_arg,second_arg,...,last_arg).

2. How can currying be achieved in C++?

2、C++如何实现柯里化?

The following is a little bit more complicated, but works very well for me (using c++11)... It also allows currying of arbitrary degree like so: auto curried=curry(f)(arg1)(arg2)(arg3)and later auto result=curried(arg4)(arg5). Here it goes:

以下有点复杂,但对我来说效果很好(使用c++ 11)......它还允许像这样任意程度的柯里化:auto curried=curry(f)(arg1)(arg2)(arg3)和以后auto result=curried(arg4)(arg5)。它是这样的:

#include <functional>

namespace _dtl {

    template <typename FUNCTION> struct
    _curry;

    // specialization for functions with a single argument
    template <typename R,typename T> struct
    _curry<std::function<R(T)>> {
        using
        type = std::function<R(T)>;

        const type
        result;

        _curry(type fun) : result(fun) {}

    };

    // recursive specialization for functions with more arguments
    template <typename R,typename T,typename...Ts> struct
    _curry<std::function<R(T,Ts...)>> {
        using
        remaining_type = typename _curry<std::function<R(Ts...)> >::type;

        using
        type = std::function<remaining_type(T)>;

        const type
        result;

        _curry(std::function<R(T,Ts...)> fun)
        : result (
            [=](const T& t) {
                return _curry<std::function<R(Ts...)>>(
                    [=](const Ts&...ts){ 
                        return fun(t, ts...); 
                    }
                ).result;
            }
        ) {}
    };
}

template <typename R,typename...Ts> auto
curry(const std::function<R(Ts...)> fun)
-> typename _dtl::_curry<std::function<R(Ts...)>>::type
{
    return _dtl::_curry<std::function<R(Ts...)>>(fun).result;
}

template <typename R,typename...Ts> auto
curry(R(* const fun)(Ts...))
-> typename _dtl::_curry<std::function<R(Ts...)>>::type
{
    return _dtl::_curry<std::function<R(Ts...)>>(fun).result;
}

#include <iostream>

void 
f(std::string a,std::string b,std::string c)
{
    std::cout << a << b << c;
}

int 
main() {
    curry(f)("Hello ")("functional ")("world!");
    return 0;
}

View output

查看输出

OK, as Samer commented, I should add some explanations as to how this works. The actual implementation is done in the _dtl::_curry, while the template functions curryare only convenience wrappers. The implementation is recursive over the arguments of the std::functiontemplate argument FUNCTION.

好的,正如 Samer 评论的那样,我应该添加一些关于它是如何工作的解释。实际的实现是在 中完成的_dtl::_curry,而模板函数curry只是方便的包装器。该实现是对std::function模板参数的参数进行递归的FUNCTION

For a function with only a single argument, the result is identical to the original function.

对于只有一个参数的函数,结果与原始函数相同。

        _curry(std::function<R(T,Ts...)> fun)
        : result (
            [=](const T& t) {
                return _curry<std::function<R(Ts...)>>(
                    [=](const Ts&...ts){ 
                        return fun(t, ts...); 
                    }
                ).result;
            }
        ) {}

Here the tricky thing: For a function with more arguments, we return a lambda whose argument is bound to the first argument to the call to fun. Finally, the remaining currying for the remaining N-1arguments is delegated to the implementation of _curry<Ts...>with one less template argument.

棘手的事情是:对于具有更多参数的函数,我们返回一个 lambda,其参数绑定到调用 的第一个参数fun。最后,剩余N-1参数的剩余柯里化被委托给_curry<Ts...>少一个模板参数的实现。

Update for c++14 / 17:

C++14 / 17 的更新:

A new idea to approach the problem of currying just came to me... With the introduction of if constexprinto c++17 (and with the help of void_tto determine if a function is fully curried), things seem to get a lot easier:

一个解决柯里化问题的新想法刚刚出现在我身上......随着if constexprc++17的引入(并在void_t确定函数是否完全柯里化的帮助下),事情似乎变得容易了很多:

template< class, class = std::void_t<> > struct 
needs_unapply : std::true_type { };

template< class T > struct 
needs_unapply<T, std::void_t<decltype(std::declval<T>()())>> : std::false_type { };

template <typename F> auto
curry(F&& f) {
  /// Check if f() is a valid function call. If not we need 
  /// to curry at least one argument:
  if constexpr (needs_unapply<decltype(f)>::value) {
       return [=](auto&& x) {
            return curry(
                [=](auto&&...xs) -> decltype(f(x,xs...)) {
                    return f(x,xs...);
                }
            );
        };    
  }
  else {  
    /// If 'f()' is a valid call, just call it, we are done.
    return f();
  }
}

int 
main()
{
  auto f = [](auto a, auto b, auto c, auto d) {
    return a  * b * c * d;
  };

  return curry(f)(1)(2)(3)(4);
}

See code in action on here. With a similar approach, hereis how to curry functions with arbitrary number of arguments.

请参阅此处的操作代码。使用类似的方法,这里是如何使用任意数量的参数柯里化函数。

The same idea seems to work out also in C++14, if we exchange the constexpr ifwith a template selection depending on the test needs_unapply<decltype(f)>::value:

同样的想法似乎也适用于 C++14,如果我们constexpr if根据测试与模板选择交换needs_unapply<decltype(f)>::value

template <typename F> auto
curry(F&& f);

template <bool> struct
curry_on;

template <> struct
curry_on<false> {
    template <typename F> static auto
    apply(F&& f) {
        return f();
    }
};

template <> struct
curry_on<true> {
    template <typename F> static auto 
    apply(F&& f) {
        return [=](auto&& x) {
            return curry(
                [=](auto&&...xs) -> decltype(f(x,xs...)) {
                    return f(x,xs...);
                }
            );
        };
    }
};

template <typename F> auto
curry(F&& f) {
    return curry_on<needs_unapply<decltype(f)>::value>::template apply(f);
}

回答by Aaron

Simplifying Gregg's example, using tr1:

使用 tr1 简化 Gregg 的示例:

#include <functional> 
using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;

int f(int, int);
..
int main(){
    function<int(int)> g     = bind(f, _1, 5); // g(x) == f(x, 5)
    function<int(int)> h     = bind(f, 2, _1); // h(x) == f(2, x)
    function<int(int,int)> j = bind(g, _2);    // j(x,y) == g(y)
}

Tr1 functional components allow you to write rich functional-style code in C++. As well, C++0x will allow for in-line lambda functions to do this as well:

Tr1 函数式组件允许您用 C++ 编写丰富的函数式代码。同样,C++0x 也允许内联 lambda 函数执行此操作:

int f(int, int);
..
int main(){
    auto g = [](int x){ return f(x,5); };      // g(x) == f(x, 5)
    auto h = [](int x){ return f(2,x); };      // h(x) == f(2, x)
    auto j = [](int x, int y){ return g(y); }; // j(x,y) == g(y)
}

And while C++ doesn't provide the rich side-effect analysis that some functional-oriented programming languages perform, const analysis and C++0x lambda syntax can help:

虽然 C++ 没有提供一些面向函数的编程语言执行的丰富的副作用分析,但 const 分析和 C++0x lambda 语法可以提供帮助:

struct foo{
    int x;
    int operator()(int y) const {
        x = 42; // error!  const function can't modify members
    }
};
..
int main(){
    int x;
    auto f = [](int y){ x = 42; }; // error! lambdas don't capture by default.
}

Hope that helps.

希望有帮助。

回答by Konrad Rudolph

Have a look at Boost.Bindwhich makes the process shown by Greg more versatile:

看看Boost.Bind,它使 Greg 展示的过程更加通用:

transform(a.begin(), a.end(), back_inserter(b), bind(f, _1, 5));

This binds 5to f's second argument.

这绑定5f的第二个参数。

It's worth noting that this is notcurrying (instead, it's partial application). However, using currying in a general way is hard in C++ (in fact, it only recently became possible at all) and partial application is often used instead.

值得注意的是,这不是柯里化(而是部分应用)。然而,在 C++ 中以通用方式使用柯里化是困难的(事实上,它直到最近才成为可能)并且经常使用部分应用来代替。

回答by kasoki

If you're using C++14 it's very easy:

如果您使用的是 C++14,则非常简单:

template<typename Function, typename... Arguments>
auto curry(Function function, Arguments... args) {
    return [=](auto... rest) {
        return function(args..., rest...);
    }; // don't forget semicolumn
}

You can then use it like this:

然后你可以像这样使用它:

auto add = [](auto x, auto y) { return x + y; }

// curry 4 into add
auto add4 = curry(add, 4);

add4(6); // 10

回答by missingfaktor

Other answers nicely explain binders, so I won't repeat that part here. I will only demonstrate how currying and partial application can be done with lambdas in C++0x.

其他答案很好地解释了活页夹,所以我不会在这里重复那部分。我将只演示如何在 C++0x 中使用 lambda 来完成柯里化和部分应用。

Code example:(Explanation in comments)

代码示例:(注释中的解释)

#include <iostream>
#include <functional>

using namespace std;

const function<int(int, int)> & simple_add = 
  [](int a, int b) -> int {
    return a + b;
  };

const function<function<int(int)>(int)> & curried_add = 
  [](int a) -> function<int(int)> {
    return [a](int b) -> int {
      return a + b;
    };
  };

int main() {
  // Demonstrating simple_add
  cout << simple_add(4, 5) << endl; // prints 9

  // Demonstrating curried_add
  cout << curried_add(4)(5) << endl; // prints 9

  // Create a partially applied function from curried_add
  const auto & add_4 = curried_add(4);
  cout << add_4(5) << endl; // prints 9
}

回答by John Millikin

Currying is a way of reducing a function that takes multiple arguments into a sequence of nested functions with one argument each:

柯里化是一种将接受多个参数的函数归约为一系列嵌套函数的方法,每个嵌套函数只有一个参数:

full = (lambda a, b, c: (a + b + c))
print full (1, 2, 3) # print 6

# Curried style
curried = (lambda a: (lambda b: (lambda c: (a + b + c))))
print curried (1)(2)(3) # print 6

Currying is nice because you can define functions that are simply wrappers around other functions with pre-defined values, and then pass around the simplified functions. C++ STL binders provide an implementation of this in C++.

柯里化很好,因为您可以定义函数,这些函数只是使用预定义值封装其他函数,然后传递简化的函数。C++ STL 绑定器在 C++ 中提供了一个实现。

回答by AndyG

Some great answers here. I thought I would add my own because it was fun to play around with the concept.

这里有一些很棒的答案。我想我会添加我自己的,因为玩这个概念很有趣。

Partial function application: The process of "binding" a function with only some of its parameters, deferring the rest to be filled in later. The result is another function with fewer parameters.

部分函数应用:只用它的一些参数“绑定”一个函数的过程,其余的推迟到以后填充。结果是另一个参数更少的函数。

Currying: Is a special form of partial function application where you can only "bind" a single argument at a time. The result is another function with exactly 1 fewer parameter.

Currying:是一种特殊形式的部分函数应用程序,您一次只能“绑定”一个参数。结果是另一个函数只少了 1 个参数。

The code I'm about to present is partial function applicationfrom which currying is possible, but not the only possibility. It offers a few benefits over the above currying implementations (mainly because it's partial function application and not currying, heh).

我将要展示的代码是部分函数应用程序,从中可以进行柯里化,但不是唯一的可能性。与上述柯里化实现相比,它提供了一些好处(主要是因为它是部分函数应用程序而不是柯里化,呵呵)。

  • Applying over an empty function:

    auto sum0 = [](){return 0;};
    std::cout << partial_apply(sum0)() << std::endl;
    
  • Applying multiple arguments at a time:

    auto sum10 = [](int a, int b, int c, int d, int e, int f, int g, int h, int i, int j){return a+b+c+d+e+f+g+h+i+j;};
    std::cout << partial_apply(sum10)(1)(1,1)(1,1,1)(1,1,1,1) << std::endl; // 10
    
  • constexprsupport that allows for compile-time static_assert:

    static_assert(partial_apply(sum0)() == 0);
    
  • A useful error message if you accidentally go too far in providing arguments:

    auto sum1 = [](int x){ return x;};
    partial_apply(sum1)(1)(1);
    

    error: static_assert failed "Attempting to apply too many arguments!"

  • 在空函数上应用:

    auto sum0 = [](){return 0;};
    std::cout << partial_apply(sum0)() << std::endl;
    
  • 一次应用多个参数:

    auto sum10 = [](int a, int b, int c, int d, int e, int f, int g, int h, int i, int j){return a+b+c+d+e+f+g+h+i+j;};
    std::cout << partial_apply(sum10)(1)(1,1)(1,1,1)(1,1,1,1) << std::endl; // 10
    
  • constexpr允许编译时的支持static_assert

    static_assert(partial_apply(sum0)() == 0);
    
  • 如果您在提供参数时不小心走得太远,则会显示有用的错误消息:

    auto sum1 = [](int x){ return x;};
    partial_apply(sum1)(1)(1);
    

    错误:static_assert 失败“试图应用太多参数!”

Other answers above return lambdas that bind an argument and then return further lambdas. This approach wraps that essential functionality into a callable object. Definitions for operator()allow the internal lambda to be called. Variadic templates allow us to check for someone going too far, and an implicit conversion function to the result type of the function call allows us to print the result or compare the object to a primitive.

上面的其他答案返回绑定参数的 lambdas,然后返回更多的 lambdas。这种方法将基本功能包装到一个可调用对象中。operator()允许调用内部 lambda 的定义。可变参数模板允许我们检查是否有人走得太远,到函数调用的结果类型的隐式转换函数允许我们打印结果或将对象与原始对象进行比较。

Code:

代码:

namespace detail{
template<class F>
using is_zero_callable = decltype(std::declval<F>()());

template<class F>
constexpr bool is_zero_callable_v = std::experimental::is_detected_v<is_zero_callable, F>;
}

template<class F>
struct partial_apply_t
{
    template<class... Args>
    constexpr auto operator()(Args... args)
    {
        static_assert(sizeof...(args) == 0 || !is_zero_callable, "Attempting to apply too many arguments!");
        auto bind_some = [=](auto... rest) -> decltype(myFun(args..., rest...))
        {
           return myFun(args..., rest...);
        };
        using bind_t = decltype(bind_some);

        return partial_apply_t<bind_t>{bind_some};
    }
    explicit constexpr partial_apply_t(F fun) : myFun(fun){}

    constexpr operator auto()
    {
        if constexpr (is_zero_callable)
            return myFun();
        else
            return *this; // a callable
    }
    static constexpr bool is_zero_callable = detail::is_zero_callable_v<F>;
    F myFun;
};

Live Demo

现场演示

A few more notes:

还有一些注意事项:

  • I chose to use is_detectedmainly for enjoyment and practice; it serves the same as a normal type trait would here.
  • There could definitely be more work done to support perfect forwarding for performance reasons
  • The code is C++17 because it requires for constexprlambda support in C++17
    • And it seems that GCC 7.0.1 is not quite there yet, either, so I used Clang 5.0.0
  • 我选择使用is_detected主要是为了享受和练习;它的作用与此处的普通类型特征相同。
  • 出于性能原因,肯定可以做更多的工作来支持完美转发
  • 代码是 C++17,因为它需要C++17 中的constexprlambda 支持
    • 似乎 GCC 7.0.1 还没有完全出现,所以我使用了 Clang 5.0.0

Some tests:

一些测试:

auto sum0 = [](){return 0;};
auto sum1 = [](int x){ return x;};
auto sum2 = [](int x, int y){ return x + y;};
auto sum3 = [](int x, int y, int z){ return x + y + z; };
auto sum10 = [](int a, int b, int c, int d, int e, int f, int g, int h, int i, int j){return a+b+c+d+e+f+g+h+i+j;};

std::cout << partial_apply(sum0)() << std::endl; //0
static_assert(partial_apply(sum0)() == 0, "sum0 should return 0");
std::cout << partial_apply(sum1)(1) << std::endl; // 1
std::cout << partial_apply(sum2)(1)(1) << std::endl; // 2
std::cout << partial_apply(sum3)(1)(1)(1) << std::endl; // 3
static_assert(partial_apply(sum3)(1)(1)(1) == 3, "sum3 should return 3");
std::cout << partial_apply(sum10)(1)(1,1)(1,1,1)(1,1,1,1) << std::endl; // 10
//partial_apply(sum1)(1)(1); // fails static assert
auto partiallyApplied = partial_apply(sum3)(1)(1);
std::function<int(int)> finish_applying = partiallyApplied;
std::cout << std::boolalpha << (finish_applying(1) == 3) << std::endl; // true

auto plus2 = partial_apply(sum3)(1)(1);
std::cout << std::boolalpha << (plus2(1) == 3) << std::endl; // true
std::cout << std::boolalpha << (plus2(3) == 5) << std::endl; // true

回答by Gabriel Garcia

I implemented currying with variadic templates as well (see Julian's answer). However, I did not make use of recursion or std::function. Note: It uses a number of C++14features.

我也使用可变参数模板实现了柯里化(参见 Julian 的回答)。但是,我没有使用递归或std::function. 注意:它使用了许多C++14特性。

The provided example (mainfunction) actually runs at compile time, proving that the currying method does not trump essential optimizations by the compiler.

提供的示例(main函数)实际上在编译时运行,证明柯里化方法并没有胜过编译器的基本优化。

The code can be found here: https://gist.github.com/Garciat/c7e4bef299ee5c607948

代码可以在这里找到:https: //gist.github.com/Garciat/c7e4bef299ee5c607948

with this helper file: https://gist.github.com/Garciat/cafe27d04cfdff0e891e

使用此帮助文件:https: //gist.github.com/Garciat/cafe27d04cfdff0e891e

The code still needs (a lot of) work, which I may or may not complete soon. Either way, I'm posting this here for future reference.

代码仍然需要(很多)工作,我可能会也可能不会很快完成。无论哪种方式,我都在这里发布以供将来参考。

Posting code in case links die (though they shouldn't):

发布代码以防链接失效(尽管它们不应该):

#include <type_traits>
#include <tuple>
#include <functional>
#include <iostream>

// ---

template <typename FType>
struct function_traits;

template <typename RType, typename... ArgTypes>
struct function_traits<RType(ArgTypes...)> {
    using arity = std::integral_constant<size_t, sizeof...(ArgTypes)>;

    using result_type = RType;

    template <size_t Index>
    using arg_type = typename std::tuple_element<Index, std::tuple<ArgTypes...>>::type;
};

// ---

namespace details {
    template <typename T>
    struct function_type_impl
      : function_type_impl<decltype(&T::operator())>
    { };

    template <typename RType, typename... ArgTypes>
    struct function_type_impl<RType(ArgTypes...)> {
        using type = RType(ArgTypes...);
    };

    template <typename RType, typename... ArgTypes>
    struct function_type_impl<RType(*)(ArgTypes...)> {
        using type = RType(ArgTypes...);
    };

    template <typename RType, typename... ArgTypes>
    struct function_type_impl<std::function<RType(ArgTypes...)>> {
        using type = RType(ArgTypes...);
    };

    template <typename T, typename RType, typename... ArgTypes>
    struct function_type_impl<RType(T::*)(ArgTypes...)> {
        using type = RType(ArgTypes...);
    };

    template <typename T, typename RType, typename... ArgTypes>
    struct function_type_impl<RType(T::*)(ArgTypes...) const> {
        using type = RType(ArgTypes...);
    };
}

template <typename T>
struct function_type
  : details::function_type_impl<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
{ };

// ---

template <typename Args, typename Params>
struct apply_args;

template <typename HeadArgs, typename... Args, typename HeadParams, typename... Params>
struct apply_args<std::tuple<HeadArgs, Args...>, std::tuple<HeadParams, Params...>>
  : std::enable_if<
        std::is_constructible<HeadParams, HeadArgs>::value,
        apply_args<std::tuple<Args...>, std::tuple<Params...>>
    >::type
{ };

template <typename... Params>
struct apply_args<std::tuple<>, std::tuple<Params...>> {
    using type = std::tuple<Params...>;
};

// ---

template <typename TupleType>
struct is_empty_tuple : std::false_type { };

template <>
struct is_empty_tuple<std::tuple<>> : std::true_type { };

// ----

template <typename FType, typename GivenArgs, typename RestArgs>
struct currying;

template <typename FType, typename... GivenArgs, typename... RestArgs>
struct currying<FType, std::tuple<GivenArgs...>, std::tuple<RestArgs...>> {
    std::tuple<GivenArgs...> given_args;

    FType func;

    template <typename Func, typename... GivenArgsReal>
    constexpr
    currying(Func&& func, GivenArgsReal&&... args) :
      given_args(std::forward<GivenArgsReal>(args)...),
      func(std::move(func))
    { }

    template <typename... Args>
    constexpr
    auto operator() (Args&&... args) const& {
        using ParamsTuple = std::tuple<RestArgs...>;
        using ArgsTuple = std::tuple<Args...>;

        using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;

        using CanExecute = is_empty_tuple<RestArgsPrime>;

        return apply(CanExecute{}, std::make_index_sequence<sizeof...(GivenArgs)>{}, std::forward<Args>(args)...);
    }

    template <typename... Args>
    constexpr
    auto operator() (Args&&... args) && {
        using ParamsTuple = std::tuple<RestArgs...>;
        using ArgsTuple = std::tuple<Args...>;

        using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;

        using CanExecute = is_empty_tuple<RestArgsPrime>;

        return std::move(*this).apply(CanExecute{}, std::make_index_sequence<sizeof...(GivenArgs)>{}, std::forward<Args>(args)...);
    }

private:
    template <typename... Args, size_t... Indices>
    constexpr
    auto apply(std::false_type, std::index_sequence<Indices...>, Args&&... args) const& {
        using ParamsTuple = std::tuple<RestArgs...>;
        using ArgsTuple = std::tuple<Args...>;

        using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;

        using CurryType = currying<FType, std::tuple<GivenArgs..., Args...>, RestArgsPrime>;

        return CurryType{ func, std::get<Indices>(given_args)..., std::forward<Args>(args)... };
    }

    template <typename... Args, size_t... Indices>
    constexpr
    auto apply(std::false_type, std::index_sequence<Indices...>, Args&&... args) && {
        using ParamsTuple = std::tuple<RestArgs...>;
        using ArgsTuple = std::tuple<Args...>;

        using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;

        using CurryType = currying<FType, std::tuple<GivenArgs..., Args...>, RestArgsPrime>;

        return CurryType{ std::move(func), std::get<Indices>(std::move(given_args))..., std::forward<Args>(args)... };
    }

    template <typename... Args, size_t... Indices>
    constexpr
    auto apply(std::true_type, std::index_sequence<Indices...>, Args&&... args) const& {
        return func(std::get<Indices>(given_args)..., std::forward<Args>(args)...);
    }

    template <typename... Args, size_t... Indices>
    constexpr
    auto apply(std::true_type, std::index_sequence<Indices...>, Args&&... args) && {
        return func(std::get<Indices>(std::move(given_args))..., std::forward<Args>(args)...);
    }
};

// ---

template <typename FType, size_t... Indices>
constexpr
auto curry(FType&& func, std::index_sequence<Indices...>) {
    using RealFType = typename function_type<FType>::type;
    using FTypeTraits = function_traits<RealFType>;

    using CurryType = currying<FType, std::tuple<>, std::tuple<typename FTypeTraits::template arg_type<Indices>...>>;

    return CurryType{ std::move(func) };
}

template <typename FType>
constexpr
auto curry(FType&& func) {
    using RealFType = typename function_type<FType>::type;
    using FTypeArity = typename function_traits<RealFType>::arity;

    return curry(std::move(func), std::make_index_sequence<FTypeArity::value>{});
}

// ---

int main() {
    auto add = curry([](int a, int b) { return a + b; });

    std::cout << add(5)(10) << std::endl;
}

回答by Rolf

These Links are relevant:

这些链接是相关的:

The Lambda Calculus page on Wikipedia has a clear example of currying
http://en.wikipedia.org/wiki/Lambda_calculus#Motivation

维基百科上的 Lambda Calculus 页面有一个清晰的柯里化示例
http://en.wikipedia.org/wiki/Lambda_calculus#Motivation

This paper treats currying in C/C++
http://asg.unige.ch/site/papers/Dami91a.pdf

本文讨论了 C/C++ 中的柯里化
http://asg.unige.ch/site/papers/Dami91a.pdf