我应该如何制作函数咖喱

How should I make function curry?

本文关键字:函数 何制作 我应该      更新时间:2023-10-16

在C++14中,咖喱函数或函数对象的好方法是什么?

特别是,我有一个重载函数foo具有一些随机数量的重载:一些重载可以通过 ADL 找到,其他重载可能在无数地方定义。

我有一个帮助程序对象:

static struct {
  template<class...Args>
  auto operator()(Args&&...args)const
  -> decltype(foo(std::forward<Args>(args)...))
    { return (foo(std::forward<Args>(args)...));}
} call_foo;

这让我可以将重载集作为单个对象传递。

如果我想咖喱foo,我应该怎么做?

由于curry和部分函数应用程序经常互换使用,curry我的意思是如果foo(a,b,c,d)是有效的调用,那么curry(call_foo)(a)(b)(c)(d)必须是有效的调用。

这是我

目前最好的尝试。

#include <iostream>
#include <utility>
#include <memory>

SFINAE 实用程序帮助程序类:

template<class T>struct voider{using type=void;};
template<class T>using void_t=typename voider<T>::type;

一个特征类,告诉您 Sig 是否是有效的调用 - 即,如果std::result_of<Sig>::type是定义的行为。 在某些C++实现中,只需检查std::result_of就足够了,但标准并不要求这样做:

template<class Sig,class=void>
struct is_invokable:std::false_type {};
template<class F, class... Ts>
struct is_invokable<
  F(Ts...),
  void_t<decltype(std::declval<F>()(std::declval<Ts>()...))>
>:std::true_type {
  using type=decltype(std::declval<F>()(std::declval<Ts>()...));
};
template<class Sig>
using invoke_result=typename is_invokable<Sig>::type;
template<class T> using type=T;

咖喱助手是一种手动 lambda。 它捕获一个函数和一个参数。它不是写成 lambda 的,因此我们可以在右值上下文中使用时启用正确的右值转发,这在 curry 时很重要:

template<class F, class T>
struct curry_helper {
  F f;
  T t;
  template<class...Args>
  invoke_result< type<F const&>(T const&, Args...) >
  operator()(Args&&...args)const&
  {
    return f(t, std::forward<Args>(args)...);
  }
  template<class...Args>
  invoke_result< type<F&>(T const&, Args...) >
  operator()(Args&&...args)&
  {
    return f(t, std::forward<Args>(args)...);
  }
  template<class...Args>
  invoke_result< type<F>(T const&, Args...) >
  operator()(Args&&...args)&&
  {
    return std::move(f)(std::move(t), std::forward<Args>(args)...);
  }
};

肉和土豆:

template<class F>
struct curry_t {
  F f;
  template<class Arg>
  using next_curry=curry_t< curry_helper<F, std::decay_t<Arg> >;
  // the non-terminating cases.  When the signature passed does not evaluate
  // we simply store the value in a curry_helper, and curry the result:
  template<class Arg,class=std::enable_if_t<!is_invokable<type<F const&>(Arg)>::value>>
  auto operator()(Arg&& arg)const&
  {
    return next_curry<Arg>{{ f, std::forward<Arg>(arg) }};
  }
  template<class Arg,class=std::enable_if_t<!is_invokable<type<F&>(Arg)>::value>>
  auto operator()(Arg&& arg)&
  {
    return next_curry<Arg>{{ f, std::forward<Arg>(arg) }};
  }
  template<class Arg,class=std::enable_if_t<!is_invokable<F(Arg)>::value>>
  auto operator()(Arg&& arg)&&
  {
    return next_curry<Arg>{{ std::move(f), std::forward<Arg>(arg) }};
  }
  // These are helper functions that make curry(blah)(a,b,c) somewhat of a shortcut for curry(blah)(a)(b)(c)
  // *if* the latter is valid, *and* it isn't just directly invoked.  Not quite, because this can *jump over*
  // terminating cases...
  template<class Arg,class...Args,class=std::enable_if_t<!is_invokable<type<F const&>(Arg,Args...)>::value>>
  auto operator()(Arg&& arg,Args&&...args)const&
  {
    return next_curry<Arg>{{ f, std::forward<Arg>(arg) }}(std::forward<Args>(args)...);
  }
  template<class Arg,class...Args,class=std::enable_if_t<!is_invokable<type<F&>(Arg,Args...)>::value>>
  auto operator()(Arg&& arg,Args&&...args)&
  {
    return next_curry<Arg>{{ f, std::forward<Arg>(arg) }}(std::forward<Args>(args)...);
  }
  template<class Arg,class...Args,class=std::enable_if_t<!is_invokable<F(Arg,Args...)>::value>>
  auto operator()(Arg&& arg,Args&&...args)&&
  {
    return next_curry<Arg>{{ std::move(f), std::forward<Arg>(arg) }}(std::forward<Args>(args)...);
  }
  // The terminating cases.  If we run into a case where the arguments would evaluate, this calls the underlying curried function:
  template<class... Args,class=std::enable_if_t<is_invokable<type<F const&>(Args...)>::value>>
  auto operator()(Args&&... args,...)const&
  {
    return f(std::forward<Args>(args)...);
  }
  template<class... Args,class=std::enable_if_t<is_invokable<type<F&>(Args...)>::value>>
  auto operator()(Args&&... args,...)&
  {
    return f(std::forward<Args>(args)...);
  }
  template<class... Args,class=std::enable_if_t<is_invokable<F(Args...)>::value>>
  auto operator()(Args&&... args,...)&&
  {
    return std::move(f)(std::forward<Args>(args)...);
  }
};
template<class F>
curry_t<typename std::decay<F>::type> curry( F&& f ) { return {std::forward<F>(f)}; }

最后一个功能幽默地简短。

请注意,不会执行键入擦除。 另请注意,理论上的手工制作解决方案可以有更少的move,但我认为我不必要地复制任何地方。

下面是一个测试函数对象:

static struct {
  double operator()(double x, int y, std::nullptr_t, std::nullptr_t)const{std::cout << "firstn"; return x*y;}
  char operator()(char c, int x)const{std::cout << "secondn"; return c+x;}
  void operator()(char const*s)const{std::cout << "hello " << s << "n";}
} foo;

还有一些测试代码,看看它是如何工作的:

int main() {
  auto f = curry(foo);
  // testing the ability to "jump over" the second overload:
  std::cout << f(3.14,10,std::nullptr_t{})(std::nullptr_t{}) << "n";
  // Call the 3rd overload:
  f("world");
  // call the 2nd overload:
  auto x =  f('a',2);
  std::cout << x << "n";
  // again:
  x =  f('a')(2);
}

现场示例

生成的代码不仅仅是一团糟。 许多方法必须重复3次才能最佳地处理&const&&&的情况。 SFINAE 条款冗长而复杂。 我最终同时使用了变参数变量,在那里使用 varargs 以确保方法中不重要的签名差异(我认为优先级较低,这并不重要,SFINAE 确保只有一个重载是有效的,除了this限定符(。

curry(call_foo)的结果是一个对象,一次可以调用一个参数,也可以一次调用多个参数。 你可以用 3 个参数调用它,然后是 1,然后是 1,然后是 2,它基本上是正确的事情。 没有证据会告诉你它想要多少参数,除了试图给它提供参数并查看调用是否有效。 这是处理超载情况所必需的。

多参数情况的一个怪癖是它不会部分地将数据包传递给一个curry,而是使用其余的作为返回值的参数。 我可以通过更改来相对容易地更改它:

    return curry_t< curry_helper<F, std::decay_t<Arg> >>{{ f, std::forward<Arg>(arg) }}(std::forward<Args>(args)...);

    return (*this)(std::forward<Arg>(arg))(std::forward<Args>(args)...);

和另外两个类似的。 这将防止"跳过"本来有效的过载的技术。 思潮? 这意味着curry(foo)(a,b,c)在逻辑上与看起来优雅curry(foo)(a)(b)(c)相同。

感谢@Casey他的回答启发了这一切。


最新修订版。 它使(a,b,c)的行为与(a)(b)(c)非常相似,除非它是直接工作的调用。

#include <type_traits>
#include <utility>
template<class...>
struct voider { using type = void; };
template<class...Ts>
using void_t = typename voider<Ts...>::type;
template<class T>
using decay_t = typename std::decay<T>::type;
template<class Sig,class=void>
struct is_invokable:std::false_type {};
template<class F, class... Ts>
struct is_invokable<
  F(Ts...),
  void_t<decltype(std::declval<F>()(std::declval<Ts>()...))>
>:std::true_type {};
#define RETURNS(...) decltype(__VA_ARGS__){return (__VA_ARGS__);}
template<class D>
class rvalue_invoke_support {
  D& self(){return *static_cast<D*>(this);}
  D const& self()const{return *static_cast<D const*>(this);}
public:
  template<class...Args>
  auto operator()(Args&&...args)&->
  RETURNS( invoke( this->self(), std::forward<Args>(args)... ) )
  template<class...Args>
  auto operator()(Args&&...args)const&->
  RETURNS( invoke( this->self(), std::forward<Args>(args)... ) )
  template<class...Args>
  auto operator()(Args&&...args)&&->
  RETURNS( invoke( std::move(this->self()), std::forward<Args>(args)... ) )
  template<class...Args>
  auto operator()(Args&&...args)const&&->
  RETURNS( invoke( std::move(this->self()), std::forward<Args>(args)... ) )
};
namespace curryDetails {
  // Curry helper is sort of a manual lambda.  It captures a function and one argument
  // It isn't written as a lambda so we can enable proper rvalue forwarding when it is
  // used in an rvalue context, which is important when currying:
  template<class F, class T>
  struct curry_helper: rvalue_invoke_support<curry_helper<F,T>> {
    F f;
    T t;
    template<class A, class B>
    curry_helper(A&& a, B&& b):f(std::forward<A>(a)), t(std::forward<B>(b)) {}
    template<class curry_helper, class...Args>
    friend auto invoke( curry_helper&& self, Args&&... args)->
    RETURNS( std::forward<curry_helper>(self).f( std::forward<curry_helper>(self).t, std::forward<Args>(args)... ) )
  };
}
namespace curryNS {
  // the rvalue-ref qualified function type of a curry_t:
  template<class curry>
  using function_type = decltype(std::declval<curry>().f);
  template <class> struct curry_t;
  // the next curry type if we chain given a new arg A0:
  template<class curry, class A0>
  using next_curry = curry_t<::curryDetails::curry_helper<decay_t<function_type<curry>>, decay_t<A0>>>;
  // 3 invoke_ overloads
  // The first is one argument when invoking f with A0 does not work:
  template<class curry, class A0>
  auto invoke_(std::false_type, curry&& self, A0&&a0 )->
  RETURNS(next_curry<curry, A0>{std::forward<curry>(self).f,std::forward<A0>(a0)})
  // This is the 2+ argument overload where invoking with the arguments does not work
  // invoke a chain of the top one:
  template<class curry, class A0, class A1, class... Args>
  auto invoke_(std::false_type, curry&& self, A0&&a0, A1&& a1, Args&&... args )->
  RETURNS(std::forward<curry>(self)(std::forward<A0>(a0))(std::forward<A1>(a1), std::forward<Args>(args)...))
  // This is the any number of argument overload when it is a valid call to f:
  template<class curry, class...Args>
  auto invoke_(std::true_type, curry&& self, Args&&...args )->
  RETURNS(std::forward<curry>(self).f(std::forward<Args>(args)...))
  template<class F>
  struct curry_t : rvalue_invoke_support<curry_t<F>> {
    F f;
    template<class... U>curry_t(U&&...u):f(std::forward<U>(u)...){}
    template<class curry, class...Args>
    friend auto invoke( curry&& self, Args&&...args )->
    RETURNS(invoke_(is_invokable<function_type<curry>(Args...)>{}, std::forward<curry>(self), std::forward<Args>(args)...))
  };
}
template<class F>
curryNS::curry_t<decay_t<F>> curry( F&& f ) { return {std::forward<F>(f)}; }
#include <iostream>
static struct foo_t {
  double operator()(double x, int y, std::nullptr_t, std::nullptr_t)const{std::cout << "firstn"; return x*y;}
  char operator()(char c, int x)const{std::cout << "secondn"; return c+x;}
  void operator()(char const*s)const{std::cout << "hello " << s << "n";}
} foo;
int main() {
  auto f = curry(foo);
  using C = decltype((f));
  std::cout << is_invokable<curryNS::function_type<C>(const char(&)[5])>{} << "n";
  invoke( f, "world" );
  // Call the 3rd overload:
  f("world");
  // testing the ability to "jump over" the second overload:
  std::cout << f(3.14,10,nullptr,nullptr) << "n";
  // call the 2nd overload:
  auto x = f('a',2);
  std::cout << x << "n";
  // again:
  x =  f('a')(2);
  std::cout << x << "n";
  std::cout << is_invokable<decltype(foo)(double, int)>{} << "n";
  std::cout << is_invokable<decltype(foo)(double)>{} << "n";
  std::cout << is_invokable<decltype(f)(double, int)>{} << "n";
  std::cout << is_invokable<decltype(f)(double)>{} << "n";
  std::cout << is_invokable<decltype(f(3.14))(int)>{} << "n";
  decltype(std::declval<decltype((foo))>()(std::declval<double>(), std::declval<int>())) y = {3};
  (void)y;
  // std::cout << << "n";
}

实时版本

这是我使用急切语义的尝试,即一旦积累了足够的参数来有效调用原始函数,就会返回(在 Demo at Coliru(:

namespace detail {
template <unsigned I>
struct priority_tag : priority_tag<I-1> {};
template <> struct priority_tag<0> {};
// High priority overload.
// If f is callable with zero args, return f()
template <typename F>
auto curry(F&& f, priority_tag<1>) -> decltype(std::forward<F>(f)()) {
    return std::forward<F>(f)();
}
// Low priority overload.
// Return a partial application
template <typename F>
auto curry(F f, priority_tag<0>) {
    return [f](auto&& t) {
        return curry([f,t=std::forward<decltype(t)>(t)](auto&&...args) ->
          decltype(f(t, std::forward<decltype(args)>(args)...)) {
            return f(t, std::forward<decltype(args)>(args)...);
        }, priority_tag<1>{});
    };
}
} // namespace detail
// Dispatch to the implementation overload set in namespace detail.
template <typename F>
auto curry(F&& f) {
    return detail::curry(std::forward<F>(f), detail::priority_tag<1>{});
}

以及没有急切语义的替代实现,需要额外的()来调用部分应用程序,从而可以从同一重载集访问例如f(int)f(int, int)

template <typename F>
class partial_application {
    F f_;
public:
    template <typename T>
    explicit partial_application(T&& f) :
        f_(std::forward<T>(f)) {}
    auto operator()() { return f_(); }
    template <typename T>
    auto operator()(T&&);
};
template <typename F>
auto curry_explicit(F&& f) {
    return partial_application<F>{std::forward<F>(f)};
}
template <typename F>
template <typename T>
auto partial_application<F>::operator()(T&& t) {
    return curry_explicit([f=f_,t=std::forward<T>(t)](auto&&...args) ->
      decltype(f_(t, std::forward<decltype(args)>(args)...)) {
        return f(t, std::forward<decltype(args)>(args)...);
    });
}

这不是一个微不足道的问题。获得正确的所有权语义是很棘手的。为了进行比较,让我们考虑一些 lambda 以及它们如何表达所有权:

[=]() {}         // capture by value, can't modify values of captures
[=]() mutable {} // capture by value, can modify values of captures
[&]() {}     // capture by reference, can modify values of captures
[a, &b, c = std::move(foo)]() {} // mixture of value and reference, move-only if foo is
                                 // can't modify value of a or c, but can b

默认情况下,我的实现按值捕获,在传递std::reference_wrapper<>时按引用捕获(与std::ref() std::make_tuple()的行为相同(,并按原样转发调用参数(左值保持左值,右值保持右值(。我无法为mutable决定一个令人满意的解决方案,所以所有的价值捕获都被有效地const

捕获仅移动类型使函子仅移动。这反过来意味着,如果ccurry_t并且d是仅移动类型,并且c(std::move(d))不调用捕获的函子,则c(std::move(d))绑定到左值意味着后续调用必须包含足够的参数来调用捕获的函子,或者必须将左值转换为右值(可能通过 std::move()(。这在引用限定符上需要一些注意。请记住,*this始终是一个左值,因此必须将 ref 限定符应用于operator()

无法知道捕获的函子需要多少参数,因为可能存在任意数量的重载,因此要小心。没有static_assert sizeof...(Captures) < max(N_ARGS).

总体而言,实现大约需要 70 行代码。正如评论中所讨论的,我遵循了curry(foo)(a, b, c, d)foo(a, b, c, d)(大致(等效的惯例,允许访问每个重载。


#include <cstddef>
#include <functional>
#include <type_traits>
#include <tuple>
#include <utility>
template<typename Functor>
auto curry(Functor&& f);
namespace curry_impl
{
    /* helper: typing using type = T; is tedious */
    template<typename T> struct identity { using type = T; };
    /* helper: SFINAE magic :) */
    template<typename...> struct void_t_impl : identity<void> {};
    template<typename... Ts> using void_t = typename void_t_impl<Ts...>::type;
    /* helper: true iff F(Args...) is invokable */
    template<typename Signature, typename = void> struct is_invokable                                                                    : std::false_type {};
    template<typename F, typename... Args> struct is_invokable<F(Args...), void_t<decltype(std::declval<F>()(std::declval<Args>()...))>> : std::true_type  {};
    /* helper: unwraps references wrapped by std::ref() */
    template<typename T> struct unwrap_reference                            : identity<T>  {};
    template<typename T> struct unwrap_reference<std::reference_wrapper<T>> : identity<T&> {};
    template<typename T> using unwrap_reference_t = typename unwrap_reference<T>::type;
    /* helper: same transformation as applied by std::make_tuple() *
    *         decays to value type unless wrapped with std::ref() *
    *    use: modeling value & reference captures                 *
    *         e.g. c(a) vs c(std::ref(a))                         */
    template<typename T> struct decay_reference : unwrap_reference<std::decay_t<T>> {};
    template<typename T> using decay_reference_t = typename decay_reference<T>::type;
    /* helper: true iff all template arguments are true */
    template<bool...> struct all : std::true_type {};
    template<bool... Booleans> struct all<false, Booleans...> : std::false_type {};
    template<bool... Booleans> struct all<true, Booleans...> : all<Booleans...> {};
    /* helper: std::move(u) iff T is not an lvalue                       *
    *    use: save on copies when curry_t is an rvalue                  *
    *         e.g. c(a)(b)(c) should only copy functor, a, b, etc. once */
    template<bool = false> struct move_if_value_impl       { template<typename U> auto&& operator()(U&& u) { return std::move(u); } };
    template<>             struct move_if_value_impl<true> { template<typename U> auto&  operator()(U&  u) { return u; } };
    template<typename T, typename U> auto&& move_if_value(U&& u) { return move_if_value_impl<std::is_lvalue_reference<T>{}>{}(std::forward<U>(u)); }
    /* the curry wrapper/functor */
    template<typename Functor, typename... Captures>
    struct curry_t {
        /* unfortunately, ref qualifiers don't change *this (always lvalue),   *
        * so qualifiers have to be on operator(),                             *
        * even though only invoke_impl(std::false_type, ...) needs qualifiers */
    #define OPERATOR_PARENTHESES(X, Y) 
        template<typename... Args> 
        auto operator()(Args&&... args) X { 
            return invoke_impl_##Y(is_invokable<Functor(Captures..., Args...)>{}, std::index_sequence_for<Captures...>{}, std::forward<Args>(args)...); 
        }
        OPERATOR_PARENTHESES(&,  lv)
        OPERATOR_PARENTHESES(&&, rv)
    #undef OPERATOR_PARENTHESES
        Functor functor;
        std::tuple<Captures...> captures;
    private:
        /* tag dispatch for when Functor(Captures..., Args...) is an invokable expression *
        * see above comment about duplicate code                             */
    #define INVOKE_IMPL_TRUE(X) 
        template<typename... Args, std::size_t... Is> 
        auto invoke_impl_##X(std::true_type, std::index_sequence<Is...>, Args&&... args) { 
            return functor(std::get<Is>(captures)..., std::forward<Args>(args)...); 
        }
        INVOKE_IMPL_TRUE(lv)
        INVOKE_IMPL_TRUE(rv)
    #undef INVOKE_IMPL_TRUE
        /* tag dispatch for when Functor(Capture..., Args...) is NOT an invokable expression *
        * lvalue ref qualifier version copies all captured values/references     */
        template<typename... Args, std::size_t... Is>
        auto invoke_impl_lv(std::false_type, std::index_sequence<Is...>, Args&&... args) {
            static_assert(all<std::is_copy_constructible<Functor>{}, std::is_copy_constructible<Captures>{}...>{}, "all captures must be copyable or curry_t must an rvalue");
            return curry_t<Functor, Captures..., decay_reference_t<Args>...>{
                functor,
                std::tuple<Captures..., decay_reference_t<Args>...>{
                    std::get<Is>(captures)...,
                    std::forward<Args>(args)...
                }
            };
        }
        /* tag dispatch for when F(As..., Args...) is NOT an invokable expression        *
        * rvalue ref qualifier version moves all captured values, copies all references */
        template<typename... Args, std::size_t... Is>
        auto invoke_impl_rv(std::false_type, std::index_sequence<Is...>, Args&&... args) {
            return curry_t<Functor, Captures..., decay_reference_t<Args>...>{
                move_if_value<Functor>(functor),
                std::tuple<Captures..., decay_reference_t<Args>...>{
                    move_if_value<Captures>(std::get<Is>(captures))...,
                    std::forward<Args>(args)...
                }
            };
        }
    };
}
/* helper function for creating curried functors */
template<typename Functor>
auto curry(Functor&& f) {
    return curry_impl::curry_t<curry_impl::decay_reference_t<Functor>>{std::forward<Functor>(f), {}};
}

在 Coliru 上演示引用限定符语义的现场演示。

这是我在学习可变参数模板时一直在玩的代码。
它是用于函数指针的ATD和std::function上的ATD的玩具示例。
我已经在lambdas上做了一个例子,但没有找到提取aruments的方法,所以lamnda还没有ATD(还没有(

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

//this is helper class for calling (variadic) func from std::tuple
template <typename F,typename ...Args>
struct TupleCallHelper
{
    template<int ...> struct seq {};
    template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
    template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
    template<int ...S>
    static inline auto callFunc(seq<S...>,std::tuple<Args...>& params, F f) -> 
           decltype(f(std::get<S>(params) ...))
    {
        return f(std::get<S>(params) ... );
    }
    static inline auto delayed_dispatch(F& f, std::tuple<Args... >&  args) ->  
          decltype(callFunc(typename gens<sizeof...(Args)>::type(),args , f))
    {
        return callFunc(typename gens<sizeof...(Args)>::type(),args , f);
    }
};


template <int Depth,typename F,typename ... Args>
struct CurriedImpl;
//curried base class, when all args are consumed
template <typename F,typename ... Args>
struct CurriedImpl<0,F,Args...>
{
    std::tuple<Args...> m_tuple;
    F m_func;
    CurriedImpl(const F& a_func):m_func(a_func)
    {
    }
    auto operator()() -> 
        decltype(TupleCallHelper<F,Args...>::delayed_dispatch(m_func,m_tuple))
    {
        return TupleCallHelper<F,Args...>::delayed_dispatch(m_func,m_tuple);
    }
};
template <typename F,typename ... Args>
struct CurriedImpl<-1,F,Args ... > ; //this is against weird gcc bug
//curried before all args are consumed (stores arg in tuple)
template <int Depth,typename F,typename ... Args>
struct CurriedImpl : public CurriedImpl<Depth-1,F,Args...>
{
    using parent_t = CurriedImpl<Depth-1,F,Args...>;
    CurriedImpl(const F& a_func): parent_t(a_func)
    {
    }
    template <typename First>
    auto operator()(const First& a_first) -> CurriedImpl<Depth-1,F,Args...>
    {
        std::get<sizeof...(Args)-Depth>(parent_t::m_tuple) = a_first;
        return *this;
    }
    template <typename First, typename... Rem>
    auto operator()(const First& a_first,Rem ... a_rem) -> 
         CurriedImpl<Depth-1-sizeof...(Rem),F,Args...>
    {
        CurriedImpl<Depth-1,F,Args...> r = this->operator()(a_first);
        return r(a_rem...);
    }
};

template <typename F, typename ... Args>
struct Curried: public CurriedImpl<sizeof...(Args),F,Args...>
{   
    Curried(F a_f):
        CurriedImpl<sizeof...(Args),F,Args...>(a_f)
    {
    }
};

template<typename A>
int varcout( A a_a)
{
    std::cout << a_a << "n" ;
    return 0;
}
template<typename A,typename ... Var>
int varcout( A a_a, Var ... a_var)
{
    std::cout << a_a << "n" ;
    return varcout(a_var ...);
}
template <typename F, typename ... Args>
auto curried(F(*a_f)(Args...)) -> Curried<F(*)(Args...),Args...>
{
   return Curried<F(*)(Args...),Args...>(a_f);
}
template <typename R, typename ... Args>
auto curried(std::function<R(Args ... )> a_f) -> Curried<std::function<R(Args ... )>,Args...>
{
   return Curried<std::function<R(Args ... )>,Args...>(a_f);
}
int main()
{
    //function pointers
    auto f = varcout<int,float>;
    auto curried_funcp = curried(f);
    curried_funcp(1)(10)(); 
    //atd for std::function
    std::function<int(int,float)> fun(f);
    auto curried_func = curried(fun);
    curried_func(2)(5)();
    curried_func(2,5)();//works too
    //example with std::lambda using  Curried  
    auto  lamb = [](int a , int b , std::string& s){
        std::cout << a + b << s ; 
    };
    Curried<decltype(lamb),int,int,std::string> co(lamb);
    auto var1 = co(2);
    auto var2 = var1(1," is sumn");
    var2(); //prints -> "3 is sum"
}
相关文章: