Search code examples
c++templateslambdatemplate-argument-deduction

C++ lambda as std::function in template function


Let's say we have this simplified version of my code:

template<typename R>
R Context::executeTransient(std::function<R(VkCommandBuffer)> const &commands) {
    ...
    R result = commands(commandBuffer);
    ...
    return result;
}

I tried to pass a lambda function as a parameter to the function Context::executeTransient() but it works only if I explicitly assign the lambda to a specific std::function type. This works:

std::function<int(VkCommandBuffer)> f = [](VkCommandBuffer commandBuffer) {
    printf("Test execution");
    return 1;
};
context.executeTransient(f);

The example above works but I'd like to achieve the example below because of aesthetic reasons and don't know if this is even possible:

context.executeTransient([](VkCommandBuffer commandBuffer) {
    printf("Test execution");
    return 1;
});

My only requirement is that Context::executeTransient() should accept lambdas and functions with a templated return type and input argument with some specific type e.g. VkCommandBuffer.


Solution

  • What about simply as follows ?

    template <typename F>
    auto Context::executeTransient (F const & commands) {
        ...
        auto result = commands(commandBuffer);
        ...
        return result;
    }
    

    This way your method accept both standard functions and lambdas (without converting them to standard functions, that is preferable, from the performance point of view (as far as I know)) and the return type is deduced from the use (auto).

    In you need to know the R type inside the method, you can apply decltype() to result

         auto result = commands(commandBuffer);
    
         using R = decltype(result);
    

    If you need to know the R type as template parameter of the method, its a little more complex because involve std::declval() and, unfortunately, add redundancy

    template <typename F,
              typename R = decltype(std::declval<F const &>()(commandBuffer))>
    R Context::executeTransient (F const & commands) {
        ...
        R result = commands(commandBuffer);
        ...
        return result;
    }