Function to Lambda

后端 未结 2 783
清酒与你
清酒与你 2021-02-06 18:54

It\'s often useful in C++ to work with lambdas and function objects, rather than function pointers. The reason being that a lambda\'s or function object\'s type fully encodes wh

相关标签:
2条回答
  • 2021-02-06 19:28

    If you just want to wrap a function pointer in a function object, I'd recommend a macro:

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

    To be used like:

    auto lam = WRAP_FN(foo);
    

    The advantage of the macro is that it'll additionally handle overloaded names - so that you can pass overloaded functions into std algorithms and have that do what you want it to.

    0 讨论(0)
  • 2021-02-06 19:31

    Given the sample usage of your code;

    makeLambdaHelper<decltype(&f)>::blah<f>();
    

    You declare a class, with the signature of the function f (using a decltype), then you again reference the function f in the call to the function blah - you reference f twice, once for the value (the pointer) and once for the type (the signature). I think you could minimise the syntax here and combine them (using template type deduction).

    void f(int, int) { std::cout << "f\n"; }
    
    template <class R, class ... Args>
    auto make_lambda(R(*F)(Args...))
    {
        return [F] (Args && ... args) {// F pointer captured by value
          return F(std::forward<Args>(args)...);
        };
    }
    
    int main()
    {
        auto lam = make_lambda(&f);
        lam(0,0);
    }
    

    Demo code.

    The function make_lambda takes the pointer to the function f once and use template deduction to get the required signature.


    As noted in the comments, a reasonable portion of the question is the issue of not having an indirection (including the tail call) in the generated code; the indirection is dependent on a number of factors, including the compiler used, level of optimisations applied and the inline-ability of the function f to begin with. See these samples for a change in compiler, a change in the complexity of the function, and a change to the "inline-ability" of f; all produce varying results of indirection, tail calls and inlined code. As you noted in the comments, it is important that f can be (and is) inlined (this is often up to the compilers optimisations) to begin with; else, there is little difference between all the options.

    On the syntax, short of the macro, there seems to be little that can be done with class templates when the template arguments require both the type and a value (the pointer here); function templates can deduce the type based on the value. Bear in mind that if the performance of the indirection is measurable and significant in the application, then you may need to bite the bullet and use the longer form of the syntax, else favour a more maintainable shorter and obvious syntax (anyone one of them).


    Given the original syntax, it could be cleaned up a little; not that it improves any of the fundamental issues, only that to place the type and value "near" each other in the template syntax would aid in the maintenance of the code (your results may vary of course);

    template <class F, F>
    struct lambda_maker;
    
    template <class R, class ... Args, R(*F)(Args...)>
    struct lambda_maker<R(Args...), F>
    {
      static auto make() {
        return [] (Args && ... args) {
          return F(std::forward<Args>(args)...);
        };
      }
    };
    // ...
    auto lam = lambda_maker<decltype(f), f>::make();
    lam(2, 3);
    
    0 讨论(0)
提交回复
热议问题