使用std :: result_of <f> </f>时,模板推断失败

时间:2014-12-27 13:17:31

标签: c++ templates type-deduction

我正在尝试创建一个函数,该函数调用并返回作为模板参数传递的函数的返回值:

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

这是一个ideone版本:http://ideone.com/IYM10x(它在VS2013.4中以类似的方式失败)

我已将std::result_of的参数替换为std::decaystd::remove_referencestd::remove_pointer的不同排列而没有任何运气。

如何编译call_functioncall_member_function,最好还为返回void的函数编译?

1 个答案:

答案 0 :(得分:5)

您不需要对推断的std::remove_reference类型应用std::decayFunction转换,以便将其与std::result_of一起使用。您需要的是一个类似于函数调用表达式的正确语法:

#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


  

如何使用不同数量的参数来完成这项工作?

#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


  

参数列表(Args ...)应该已经是Function的一部分,为什么我需要它们呢?或者更确切地说是有一种方法可以使std :: result_of&lt;&gt;没有他们的工作?

是的,参数列表已经是推导出的Function签名的一部分。诀窍是,这不是std::result_of<F>的工作方式 - 它只使用函数声明的语法。

std::result_of<F>用于在使用给定类型的参数调用时查询给定仿函数对象的结果类型。

函数的结果类型通常是int int(char,float),在std::result_of中,它被视为仿函数对象的类型将应用函数调用操作符。因此,如果您的Function定义如下:

using Function = int(char,float);

然后std::result_of<Function>::type等于:

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                     

也就是说,在std::result_of的部分特化中推导出的结果类型用于获取仿函数对象的实例。由于没有定义int的函数调用操作符,因此上述尝试无法编译。

如果Function被推断为对功能的引用,那么您最终会得到一个不完整的std::result_of主模板,因为它甚至不匹配任何它的部分专业化。

如果你想得到函数的返回类型(不先提供参数,那么decltype()不是一个选项),你可以在函数模板参数推导过程中推导出它:

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

或通过提供单独的特质类:

#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