Passing capturing lambda as function pointer

前端 未结 9 2229
-上瘾入骨i
-上瘾入骨i 2020-11-21 06:58

Is it possible to pass a lambda function as a function pointer? If so, I must be doing something incorrectly because I am getting a compile error.

Consider the follo

相关标签:
9条回答
  • 2020-11-21 07:35

    While the template approach is clever for various reasons, it is important to remember the lifecycle of the lambda and the captured variables. If any form of a lambda pointer is is going to be used and the lambda is not a downward continuation, then only a copying [=] lambda should used. I.e., even then, capturing a pointer to a variable on the stack is UNSAFE if the lifetime of those captured pointers (stack unwind) is shorter than the lifetime of the lambda.

    A simpler solution for capturing a lambda as a pointer is:

    auto pLamdba = new std::function<...fn-sig...>([=](...fn-sig...){...});
    

    e.g., new std::function<void()>([=]() -> void {...}

    Just remember to later delete pLamdba so ensure that you don't leak the lambda memory. Secret to realize here is that lambdas can capture lambdas (ask yourself how that works) and also that in order for std::function to work generically the lambda implementation needs to contain sufficient internal information to provide access to the size of the lambda (and captured) data (which is why the delete should work [running destructors of captured types]).

    0 讨论(0)
  • 2020-11-21 07:38

    Shafik Yaghmour's answer correctly explains why the lambda cannot be passed as a function pointer if it has a capture. I'd like to show two simple fixes for the problem.

    1. Use std::function instead of raw function pointers.

      This is a very clean solution. Note however that it includes some additional overhead for the type erasure (probably a virtual function call).

      #include <functional>
      #include <utility>
      
      struct Decide
      {
        using DecisionFn = std::function<bool()>;
        Decide(DecisionFn dec) : dec_ {std::move(dec)} {}
        DecisionFn dec_;
      };
      
      int
      main()
      {
        int x = 5;
        Decide greaterThanThree { [x](){ return x > 3; } };
      }
      
    2. Use a lambda expression that doesn't capture anything.

      Since your predicate is really just a boolean constant, the following would quickly work around the current issue. See this answer for a good explanation why and how this is working.

      // Your 'Decide' class as in your post.
      
      int
      main()
      {
        int x = 5;
        Decide greaterThanThree {
          (x > 3) ? [](){ return true; } : [](){ return false; }
        };
      }
      
    0 讨论(0)
  • 2020-11-21 07:43

    A lambda can only be converted to a function pointer if it does not capture, from the draft C++11 standard section 5.1.2 [expr.prim.lambda] says (emphasis mine):

    The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

    Note, cppreference also covers this in their section on Lambda functions.

    So the following alternatives would work:

    typedef bool(*DecisionFn)(int);
    
    Decide greaterThanThree{ []( int x ){ return x > 3; } };
    

    and so would this:

    typedef bool(*DecisionFn)();
    
    Decide greaterThanThree{ [](){ return true ; } };
    

    and as 5gon12eder points out, you can also use std::function, but note that std::function is heavy weight, so it is not a cost-less trade-off.

    0 讨论(0)
提交回复
热议问题