我应该如何使功能咖喱? [英] How should I make function curry?

查看:138
本文介绍了我应该如何使功能咖喱?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



辅助对象:

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

允许我将重载设置为单个对象。



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



code> curry 和部分函数应用程序通常可以交换使用, curry 我的意思是如果 foo 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;

一个traits类,告诉你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>使用type = T;

Curry助手是一种手动lambda。它捕获一个函数和一个参数。它不是写为lambda,所以我们可以在右值上下文中使用正确的右值转发,这在currying时非常重要:

  template< class F,class T> 
struct curry_helper {
F f;
T t;
template< class ... Args>
invoke_result<类型< F const&>(T const& Args ...)>
operator()(Args& ... args)const&
{
return f(t,std :: forward< Args>(args)...)
}
template< class ... Args>
invoke_result<类型< F&>(T const& Args ...)>
operator()(Args& ... args)&
{
return f(t,std :: forward< Args>(args)...)
}
template< class ... Args>
invoke_result<类型< 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> > ;;
//非终止的情况。当通过的签名不评价
//时,我们只需将该值存储在curry_helper中,并且将结果curry:
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)}};
}
//这些是使curry(blah)(a,b,c)在curry(blah)(a)(b) // * if *后者有效,*和*不是直接调用。不完全,因为这可以跳过*
//终止case ...
template< class Arg,class ... Args,class = std :: enable_if_t<!is_invokable< type< F const& ;>(Arg,Arg ...)> :: value>>
auto operator()(Arg&& arg,Args& ... args)const&
{
return next_curry< Arg> {{f,std :: forward< Arg>(arg)}}(std :: forward&
}
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&
}
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& );
}

//终止的情况。如果我们遇到参数将求值的情况,则调用底层的curried函数:
template< class ... Args,class = std :: enable_if_t< is_invokable< type< F const& ..)> :: 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 s,但我不认为我不必要地在任何地方复制。



这里是一个测试函数对象:

  static struct {
double operator ,int y,std :: nullptr_t,std :: nullptr_t)const {std :: cout< first\\\
;返回x * y;}
char operator()(char c,int x)const {std :: cout< second\\\
; return c + x;}
void operator()(char const * s)const {std :: cout< hello< s<< \\\
;}
} foo;

和一些测试代码看看它是如何工作的:

  int main(){
auto f = curry(foo);
//测试跳过第二个重载的能力:
std :: cout<< f(3.14,10,std :: nullptr_t {})(std :: nullptr_t {})<< \\\
;
//调用第三个重载:
f(world);
//调用第二个重载:
auto x = f('a',2);
std :: cout<< x<< \\\
;
// again:
x = f('a')(2);
}

生活示例



生成的代码是一个混乱。许多方法必须重复3次以处理& const& & ;& 案例。 SFINAE子句长且复杂。我最终使用了两个变量args varargs,varargs在那里确保一个非重要的签名差异的方法(和较低的优先级我认为,不重要,SFINAE确保只有一个重载除了 this 限定符)。



curry(call_foo) 是一个可以一次调用一个参数的对象,或者一次调用多个参数。你可以调用它有3个参数,然后1,然后1,然后2,它大部分是正确的事情。没有暴露的证据告诉你它想要多少参数,除了只是试图喂它的参数,看看调用是否有效。这是处理超载情况所必需的。



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

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

 <$ (std :: forward< Arg>(arg))(std :: forward< Args>(args)...) 

,另外两个类似的。这将防止跳过否则将是有效的过载的技术。想法?这意味着 curry(foo)(a,b,c)在逻辑上等同于 curry(foo)(a)



感谢@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__);}

模板< 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& .Args>
auto operator()(Args& ... args)const&& - >
RETURNS(invoke(std :: move(this-> self()),std :: forward< Args>(args)...))
};

命名空间curryDetails {
// Curry帮助程序是一种手动lambda。它捕获一个函数和一个参数
//它不写为lambda,所以我们可以启用适当的右值转发,当它是一个右值上下文中使用
//时,这是非常重要的curry:
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)...))
};
}

命名空间curryNS {
// curry_t的rvalue-ref限定函数类型:
template< class curry>
using function_type = decltype(std :: declval< curry>()。f);

template< class> struct curry_t;

//下一个curry类型如果我们链接给一个新的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
//当使用A0调用f不起作用时,第一个是一个参数:
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)})

//这是2+参数重载,其中用参数调用不起作用
//调用顶部链的链:
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& ...))

//当它是对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& )
};
}

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\\\
;返回x * y;}
char operator()(char c,int x)const {std :: cout< second\\\
; return c + x;}
void operator()(char const * s)const {std :: cout< hello< s<< \\\
;}
} foo;

int main(){

self f = curry(foo);
using C = decltype((f));
std :: cout<< is_invokable< curryNS :: function_type< C>(const char(&)[5])> {}< \\\
;
invoke(f,world);
//调用第三个重载:
f(world);
//测试跳过第二个重载的能力:
std :: cout<< f(3.14,10,nullptr,nullptr) \\\
;
//调用第二个重载:
auto x = f('a',2);
std :: cout<< x<< \\\
;
// again:
x = f('a')(2);
std :: cout<< x<< \\\
;
std :: cout<< is_invokable< decltype(foo)(double,int)> {}<< \\\
;
std :: cout<< is_invokable< decltype(foo)(double)> {}<< \\\
;
std :: cout<< is_invokable< decltype(f)(double,int)> {}<< \\\
;
std :: cout<< is_invokable< decltype(f)(double)> {}<< \\\
;
std :: cout<< is_invokable< decltype(f(3.14))(int)> {}<< \\\
;
decltype(std :: declval< decltype((foo))>()(std :: declval< double>(),std :: declval< int>()))y = {3};
(void)y;
// std :: cout<< << \\\
;
}

直播版本


In C++14, what is a good way to curry functions or function objects?

In particular, I have an overloaded function foo with some random number of overloads: some overloads may be found via ADL, others may be defined in a myriad of places.

I have a helper object:

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

that lets me pass the overload set around as a single object.

If I wanted to curry foo, how should I do so?

As curry and partial function application are often used interchangeably, by curry I mean if foo(a,b,c,d) is a valid call, then curry(call_foo)(a)(b)(c)(d) must be a valid call.

解决方案

Here is my current best attempt.

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

SFINAE utility helper class:

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

A traits class that tells you if Sig is a valid invokation -- ie, if std::result_of<Sig>::type is defined behavior. In some C++ implementations simply checking std::result_of is enough, but that isn't required by the standard:

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

The meat and potatoes:

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

The final function is humorously short.

Note that no type erasure is done. Also note that the theoretical hand-crafted solution can have far fewer moves, but I don't think I needlessly copy anywhere.

Here is a test function object:

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;

And some test code to see how it works:

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

live example

The resulting code is more than a bit of a mess. Lots of methods had to be repeated 3 times to handle &, const& and && cases optimally. The SFINAE clauses are long and complex. I ended up using both variardic args and varargs, with the varargs there to ensure a non-important signature difference in the method (and lower priority I think, not that it matters, the SFINAE ensures only one overload is ever valid, except this qualifiers).

The result of curry(call_foo) is an object that can be called one argument at a time, or many arguments at a time. You can call it with 3 arguments, then 1, then 1, then 2, and it does mostly the right thing. No evidence is exposed telling you how many arguments it wants, other than just trying to feed it arguments and seeing if the call is valid. This is required to handle overloading cases.

A quirk of the multiple-argument case is that it won't partially pass the packet to one curry, and use the rest as arguments to the return value. I could change that relatively easily by changing:

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

to

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

and the other two similar ones. That would prevent the technique of "jumping over" an overload that would otherwise be valid. Thoughts? It would mean that curry(foo)(a,b,c) would be logically identical to curry(foo)(a)(b)(c) which seems elegant.

Thanks to @Casey whose answer inspired much of this.


Most recent revision. It makes (a,b,c) behave much like (a)(b)(c) unless it is call that works directly.

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

live version

这篇关于我应该如何使功能咖喱?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆