Home > Software engineering >  How to get function template taking invokables to match the types?
How to get function template taking invokables to match the types?

Time:09-22

I have the following code intended to take a generic function object that takes two arguments and return a function object that does the same with the arguments in the other order.

#include <type_traits>
#include <functional>

template<typename Function, typename FirstIn
    , typename SecondIn, typename std::enable_if<std::is_invocable<Function, FirstIn, SecondIn>::value>::type>
std::function<typename std::invoke_result<Function, FirstIn, SecondIn>::type(SecondIn, FirstIn)> 
swapInput(Function f)
{
    return[=](SecondIn b, FirstIn a) { return std::invoke(f, a, b); };
}

int main()
{
    std::function<bool(std::string, int)> isLength = [](std::string s, int len) {return (s.size() == len); };
    std::function<bool(int, std::string)> lengthIs =
        swapInput<std::function<bool(std::string, int)>, std::string, int>(isLength);
}

This gives the following compiler errors at the line assigning lengthIs:

Error   C2783   'std::function<std::invoke_result<Function,FirstIn,SecondIn>::type(SecondIn,FirstIn)> swapInput(Function)'
                : could not deduce template argument for '__formal' 
Error   C2672   'swapInput': no matching overloaded function found  

I am using Visual Studio 19 set to C 17.

CodePudding user response:

Your usage of std::enable_if is wrong. You need

template<typename Function, typename FirstIn, typename SecondIn
    , typename = std::enable_if_t<
    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                    std::is_invocable_v<Function, FirstIn, SecondIn>    
                >
>
std::function<std::invoke_result_t<Function, FirstIn, SecondIn>(SecondIn, FirstIn)>
swapInput(Function f)
{
    return [=](SecondIn b, FirstIn a) {  return std::invoke(f, a, b);  };
}

(See a Demo)


Suggestions:

  • Since you are using , I would suggest a auto return for swapInput.

  • One step further, if you rearrange the function template parameters, you do not need the verbose explicit std::function<bool(std::string, int)> at the function call.

  • Using if constexpr, more easily readable code:

With the above suggestions:

template<typename FirstIn, typename SecondIn, typename Function>
auto swapInput(Function f)
{
    if constexpr (std::is_invocable_v<Function, FirstIn, SecondIn >)
        return [=](SecondIn b, FirstIn a) { return std::invoke(f, a, b); };
}

now the function call would be

std::function<bool(int, std::string)> lengthIs 
                               = swapInput<std::string, int>(isLength);

(See a Demo)

  • Related