Search code examples
c++templatestype-traits

determine result type of lambdas with different signature


Suppose I have a routine that receives a callable Lambda as parameter, and this Lambda is allowed to have 2 signatures: it can be called providing an int or 2 ints.

The problem is: how to determine the result type of this Lambda.

The following code illustrates the problem

#include <iostream>
#include <type_traits>

template <class Lambda>
auto f(Lambda&& f)
{
    using result_type = std::conditional_t<std::is_invocable_v<Lambda, int>, std::invoke_result_t<Lambda, int>, std::invoke_result_t<Lambda, int, int>>;
    result_type res;
    if constexpr (std::is_invocable_v<Lambda, int>) {
        res = f(3);
    }
    else {
        res =  f(3, 3);
    }
    
    return res;
}

int main()
{
    std::cout << f([](int x) -> int { return x; }) << std::endl;
    std::cout << f([](int x, int y) -> int { return x * y; }) << std::endl;

    return 0;
}

Check it Live on Coliru.

The code does not compiles because, inside std::conditional_t, both the 2nd and 3rd template parameters must be defined, and here clearly only one exists.

I can think about writing a traits that serves to this problem, say exploiting template specialization. Something like

template <class F, bool T>
struct helper;

template <class F>
struct helper<F, true> {
    using type = std::invoke_result_t<F, int>;
};

template <class F>
struct helper<F, false> {
    using type = std::invoke_result_t<F, int, int>;
};

But before reinventing the wheel: is there a simpler compact solution using the std library? I am interested in a c++17 solution.


Solution

  • From your code, you just have to "delay" evaluation:

    using result_type =
        std::conditional_t<std::is_invocable_v<Lambda, int>,
           std::invoke_result<Lambda, int>,
           std::invoke_result<Lambda, int, int>>::type;
    

    Demo