Как мне сделать функцию карри?

В 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) должен быть действительный звонок.

26

Решение

Вот моя лучшая попытка.

#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;

Curry helper — это своего рода ручная лямбда. Он захватывает функцию и один аргумент. Он не написан как лямбда, поэтому мы можем включить правильную пересылку rvalue, когда она используется в контексте rvalue, что важно при каррировании:

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 << "first\n"; return x*y;}
char operator()(char c, int x)const{std::cout << "second\n"; 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 длинные и сложные. Я закончил с использованием обоих variardic args а также varargs, с 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 << "first\n"; return x*y;}
char operator()(char c, int x)const{std::cout << "second\n"; 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";
}

живая версия

11

Другие решения

Вот моя попытка использовать нетерпеливую семантику, то есть возврат, как только набрано достаточное количество аргументов для правильного вызова исходной функции (Демо в Колиру):

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)...);
});
}
7

Это не тривиальная проблема. Получить право собственности на семантику сложно. Для сравнения давайте рассмотрим несколько лямбд и как они выражают право собственности:

[=]() {}         // 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::make_tuple() с std::ref()) и пересылает вызывающие аргументы как есть (lvalues ​​остаются lvalues, rvalues ​​остаются rvalues). Я не мог выбрать удовлетворительное решение для mutableТаким образом, все значения захвата фактически const,

Захват типа только для перемещения делает функтор только для перемещения. Это в свою очередь означает, что если c это curry_t а также d тип только для перемещения, и c(std::move(d)) не вызывает захваченный функтор, затем связывание c(std::move(d)) значение lvalue означает, что последующие вызовы либо должны содержать достаточно аргументов для вызова захваченного функтора, либо значение lvalue должно быть преобразовано в значение rvalue (возможно, через std::move()). Это позаботилось о ссылочных квалификаторах. Имейте в виду, что *this всегда lvalue, поэтому 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 демонстрация семантики ссылочного классификатора.

4

Вот код, с которым я играл, изучая различные шаблоны.
Это игрушечный пример для ATD для указателей на функции и ATD для std :: function.
Я привел пример с лямбдами, но пока не могу найти способ извлечения арументов, поэтому нет 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 sum\n");
var2(); //prints -> "3 is sum"}
0
По вопросам рекламы [email protected]