Home > Back-end >  Is there anyway to get a lambda's return value by deduction without passing the argument types?
Is there anyway to get a lambda's return value by deduction without passing the argument types?

Time:09-17

Consider the following example:

template<auto const fnc>
struct dummy_s {

    typedef std::invoke_result<decltype(fnc), std::uint8_t >::type return_t;

};

int main() {
    dummy_s<[](std::uint8_t const& n) -> bool { return true ^ n; }>::return_t s = true; 
}

Is there anyway to get the return type without specifying std::uint8_t or whatever the number of arguments is, as template parameter as example.

CodePudding user response:

You could write a metafunction that gives you the type of the first argument

template<typename Ret, typename Arg>
auto arg(Ret(*)(Arg)) -> Arg;

and then decay the lambda fnc to a function pointer (using say), that you pass to arg, and then use that in the typedef.

typedef std::invoke_result<decltype(fnc), 
                           decltype(arg( fnc))>::type return_t;

This will only work for lambdas that don't capture anything, and that take a single argument.


You can also considerably simplify the typedef inside the struct by simply using arg directly like this

using return_t = decltype(arg( fnc));  // using is cleaner than a typedef as well

This avoids using invoke_result entirely, and lets you define arg in a way that allows lambdas with multiple arguments to be passed to it

template<typename Ret, typename Arg, typename ...R>
auto arg(Ret(*)(Arg, R...)) -> Arg;

Here's a demo

CodePudding user response:

As I said in my comment, each time I though I needed this feature I realized later I was going the wrong path. The reason is that as soon as the lambda is a template (auto) there is no hope to make it work.

More generally, if you don't have clue of the "universe" inputs of a function you don't really have a function, conceptually speaking.

If you think you still need it, you can use Boost.TypeTraits, lambda decay and function pointers.

#include<cstdint>
#include<boost/type_traits.hpp>

int main(){
    auto f = [](std::uint8_t const& n) -> bool {return true ^ n;};
    using f_return = boost::function_traits<decltype(* f)>::result_type;
    static_assert( std::is_same<f_return, bool>{} , "!");
}

With any generalization of f, overload or templates, will not work. You are really lucky that this sort of works because of a series of quirks in the language starting from the existence of monomorphic functions (inherited from C, pointer decay, etc). Conceptually, it is horrible.

Having said that, there is also a potential problem that is very sensitive to the version of C you are using, that is the use of lambdas in template (non-evaluated) contexts.


This is working solution based on your code. It really works as is with certain combinations of compilers and flags, for example https://godbolt.org/z/5x684nfWc :

#include<cstdint>
#include<boost/type_traits.hpp>


template<auto fnc>
struct dummy_s {
    using return_t = typename boost::function_traits<decltype(* fnc)>::result_type;

};

int main() {
    typename dummy_s<[](std::uint8_t const& n) -> bool { return true ^ n; }>::return_t s = true; 
}
  • Related