【问题标题】:Template deduction fails when using std::result_of<F>使用 std::result_of<F> 时模板推导失败
【发布时间】:2014-12-27 13:17:30
【问题描述】:

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

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 的函数?

【问题讨论】:

  • 如果result_of 是这样工作的,那么当F 是一个带有重载operator() 的函子时,你会期望std::result_of&lt;F&gt;::type 是什么?你不能问“调用F的结果是什么?”你只能说“用这些参数调用 F 的结果是什么?”

标签: c++ templates type-deduction


【解决方案1】:

您无需将std::remove_referencestd::decay 转换应用于推导的Function 类型,以便与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 在没有它们的情况下工作?

是的,参数列表已经是推断出的Function 签名的一部分。诀窍是,std::result_of&lt;F&gt; 不是这样工作的——它只使用函数声明的语法。

std::result_of&lt;F&gt; 用于在使用给定类型的参数调用时查询给定函子对象的结果类型。

函数的结果类型通常是什么,例如 int 对应于 int(char,float),在 std::result_of 内,它被视为函数调用运算符将​​使用的函子对象的类型被应用于。因此,当您将Function 定义如下时:

using Function = int(char,float);

那么std::result_of&lt;Function&gt;::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

【讨论】:

  • 或者直接使用-&gt; decltype(f()), -&gt; decltype((instance-&gt;*f)())
  • 如何使这个函数适用于具有不同数量参数的函数? (暂时忽略返回语句,我已经以不同的方式实现了)
  • 参数列表 (Args...) 应该已经是 Function 的一部分,为什么我又需要它们?或者更确切地说,有没有办法让 std::result_of 在没有它们的情况下工作?我无权访问参数(只是函数类型的一部分)。
  • @PiotrS。鉴于我当前问题的表述,您的回答显然是正确的,所以我将接受它并提出一个不同的问题 - 谢谢。
  • 不转发成员函数有什么原因吗?
猜你喜欢
  • 2013-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多