Template inference doesn't work when using std :: result_of <F>
I'm trying to create a function that calls and returns the return value of a function passed as a template parameter:
template <typename Function>
typename std::result_of<Function>::type
call_function(Function&& f)
{
return f();
}
template <typename Function, typename Class>
typename std::result_of<Function>::type
call_member_function(Function&& f, Class* instance)
{
return instance->*f();
}
//
// call site:
//
call_function(f);
call_member_function(&F::f, &instance);
Here is the ideal version: http://ideone.com/IYM10x (with a similar error in VS2013.4)
I replaced the argument std::result_of
with different permutations std::decay
, std::remove_reference
and std::remove_pointer
with no luck.
How do I compile, call_function
and call_member_function
preferably also for functions that return void
?
source to share
You do not need to apply the conversion std::remove_reference
or std::decay
to the inferred type Function
to use it with std::result_of
. You need the correct syntax, similar to the function call expression:
#include <type_traits>
#include <utility>
template <typename Function>
auto call_function(Function&& f)
-> typename std::result_of<Function()>::type
// ~^
{
return std::forward<Function>(f)();
}
template <typename Function, typename Class>
auto call_member_function(Function&& f, Class* instance)
-> typename std::result_of<Function(Class*)>::type
// ~~~~~~~^
{
return (instance->*std::forward<Function>(f))();
}
How do I make this work for functions with different number of arguments?
#include <type_traits>
#include <utility>
template <typename Function, typename... Args>
auto call_function(Function&& f, Args&&... args)
-> typename std::result_of<Function(Args...)>::type
{
return std::forward<Function>(f)(std::forward<Args>(args)...);
}
template <typename Function, typename Class, typename... Args>
auto call_member_function(Function&& f, Class* instance, Args&&... args)
-> typename std::result_of<Function(Class*, Args...)>::type
{
return (instance->*std::forward<Function>(f))(std::forward<Args>(args)...);
}
The argument list (Args ...) should already be part of the function, why do I need them? Rather, is there a way to make std :: result_of <> work without them?
Yes, the parameter list is already part of the printed signature Function
. The trick is that this is not how it works std::result_of<F>
- it only uses the function declaration syntax.
std::result_of<F>
is intended to query the result type of a given functor object when called with arguments of specified types.
What will usually be the result of the function type, for example int
for int(char,float)
, internally std::result_of
it is treated as the type of the functor object to which the function call statement will be applied. Hence, if you have one Function
, defined like this:
using Function = int(char,float);
then std::result_of<Function>::type
equals:
std::result_of<int(char,float)>::type
// | | |
// | | `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.
// | `~~~~~~~~~~~~~~~~~~~~~~~~~. |
// `~~~~~~~~. | |
// V V V
decltype( std::declval<int>() ( std::declval<char>(), std::declval<float>() ) )
// ^ ^ ^~~~~~~~~~~~~~~^
// instance of a functor call operator arguments
That is, the result type inferred in the partial specialization std::result_of
is used to obtain an instance of the functor object. Since there is no function call operator for int
, the specified attempt will not compile.
If Function
outputted as a function reference, then you get an incomplete primary template std::result_of
, because it doesn't even match any partial specialization.
If you want to get the return type of the function (without providing the first arguments, so decltype()
it is not a parameter), you can deduce it during the deduction of the function template argument:
template <typename R, typename... Params, typename... Args>
R call_function(R(*f)(Params...), Args&&... args)
{
return f(std::forward<Args>(args)...);
}
template <typename R, typename Class, typename... Params, typename... Args>
R call_member_function(R(Class::*f)(Params...), Class* instance, Args&&... args)
{
return (instance->*f)(std::forward<Args>(args)...);
}
or by providing a separate class of characteristics:
#include <utility>
#include <type_traits>
template <typename F>
struct return_type;
template <typename R, typename... Args>
struct return_type<R(Args...)> { using type = R; };
template <typename R, typename... Args>
struct return_type<R(*)(Args...)> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...)> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) &> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) &&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const&&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) volatile> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) volatile&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) volatile&&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const volatile> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const volatile&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const volatile&&> { using type = R; };
template <typename Function, typename... Args>
auto call_function(Function&& f, Args&&... args)
-> typename return_type<typename std::remove_reference<Function>::type>::type
{
return std::forward<Function>(f)(std::forward<Args>(args)...);
}
template <typename Function, typename Class, typename... Args>
auto call_member_function(Function&& f, Class* instance, Args&&... args)
-> typename return_type<typename std::remove_reference<Function>::type>::type
{
return (instance->*std::forward<Function>(f))(std::forward<Args>(args)...);
}
source to share