How do I make a functor out of an arbitrary function?

为君一笑 提交于 2021-02-10 16:21:16

问题


I have a bunch of functions which I want to use as functors (that is, use types instead of pass pointers to the functions, or any other kind of data).

Is there an elegant/idiomatic/standard way of doing this with the standard library, or the standard library + Boost? Perhaps using bind() somehow?

Or should I go with something simplistic (well, kind of simplistic) such as:

template<typename Function, Function& F, typename... Parameters>
struct functor {
    using function_type            = Function;
    using parameters_as_tuple_type = std::tuple<Parameters...>;

    auto operator() (Parameters&&... params) ->
        decltype(F(std::forward<Parameters>(params)...))
    {
        return F(std::forward<Parameters>(params)...);
    }
};

Notes:

  • C++11 solutions are preferred, but if you have something requiring even C++17, that's also interesting.
  • My "solution" might not work, I think, for overloaded functions.

回答1:


First, a concrete example for a fixed type.

int foo( int );
void foo( double );

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

now foo_t is an object that invokes the overloads of foo via perfect forwarding.

To make it generic:

#define RETURNS(...) noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }

#define OVERLOAD_SET_TYPE(...) \
  struct { \
    template<class...Args> \
    auto operator()(Args&&...args)const \
    RETURNS( __VA_ARGS__( std::forward<Args>(args)... ) ) \
  }

so we can do

using foo_t = OVERLOAD_SET_TYPE(foo);

live example.

You cannot manipulate overload sets of a function as an object; the only way to do it is textually. Hence the macro.

This has all of the usual imperfections of perfect forwarding. There is no way to generically avoid those imperfections.




回答2:


If the function is not overloaded, you can do this in C++17:

template <auto F>
auto to_function_object()
{
    return [](auto&&... xs) -> decltype(auto)
    { 
        return F(std::forward<decltype(xs)>(xs)...);
    };
} 

void a(int) { }

int main()
{
    auto af = to_function_object<a>();
    af(1);
}

If the function is overloaded, there's no way to pass its overload set as an argument to another function or a template. You're forced to manually write a wrapper lambda on the spot. Example:

void foo(int)  { }
void foo(char) { }
// ...   
bar([](auto x){ return foo(x); });

N3617 aimed to solve this issue by introducing a "lift" operator.

P0119 by A. Sutton solves the problem in a different way by allowing overload sets to basically generate the "wrapper lambda" for you when passed as arguments.

Until any of those proposal is accepted you can use a beautiful C++14 macro instead:

#define LIFT(f) \
    [](auto&&... xs) noexcept(noexcept(f(std::forward<decltype(xs)>(xs)...))) \
        -> decltype(f(std::forward<decltype(xs)>(xs)...)) \
    { \
        return f(std::forward<decltype(xs)>(xs)...); \
    }


来源:https://stackoverflow.com/questions/43504566/how-do-i-make-a-functor-out-of-an-arbitrary-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!