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;
}