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

?

+1


source to share


1 answer


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

      

DEMO


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

      

DEMO 2


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

      

DEMO 3

+5


source







All Articles