如何在 C++11 中为 result_of 编写辅助类型

How to write a helper type for result_of in C++11

template<typename F,typename...X>
using Result_of = typename result_of<F(X...)>::type;

int ff(int){return 2;}
typedef bool(*PF)(int);
auto fx = [](char ch){return tolower(ch);};
Result_of<decltype(&ff)()> r1 = 7;
Result_of<PF(int)> r2 = 2;
Result_of<decltype(fx)(char)> r5 = "a";

当我用 gcc 编译时,出现以下错误:

main.cpp: In substitution of 'template<class F, class ... X> using Result_of = typename std::result_of<_Functor(_ArgTypes ...)>::type [with F = int (*())(int); X = {}]':
main.cpp:17:30:   required from here
main.cpp:6:57: error: function returning a function
 using Result_of = typename std::result_of<F(X...)>::type;
                                                         ^

为什么会出现此错误,我该如何解决?

这一行有几处错误:

Result_of<decltype(&ff)()> r1 = 7;

首先,Result_of 接受函数类型和参数列表,以逗号分隔。您不是以这种方式提供它们。所以它将 F 解释为 (int)(*)(int)(),这将是一个指向函数的指针,该函数采用 int returning 函数 returning int.但是在 C++ 中拥有函数 return 函数是非法的,因此会出现错误。

其次,ff需要一个int,而不是什么都没有。最后,ff 衰减为指向函数的指针,您需要实际的 函数 。正确的表达方式是:

Result_of<decltype(*ff), int> r1 = 7;

同理,接下来需要:

Result_of<PF, int> r2 = 2;
Result_of<decltype(fx), char> r5 = 'a'; // fx returns an int, so you 
                                        // can't assign a const char* to it

简短的回答是:

template<class Sig>
using Result_of = typename std::result_of<Sig>::type;

将使您的用例成为 "work"。有些会编译失败,因为你传入了错误的签名,或者类型不匹配。

但这不是最好的方法。

C++11 编译器并不都实现 result_of 的最佳实践版本:即 SFINAE 友好版本。如果您正在编写帮助程序,我会以最佳实践方式进行编写。

首先,别名做decltype评价:

template<class F, class...Args>
using invoke_result = decltype( std::declval<F>()(std::declval<Args>()...));

接下来,一些我觉得有用的元编程样板:

namespace details {
  template<class...>struct voider{using type=void;};
  template<class...Ts>using void_t=typename voider<Ts...>::type;

  template<template<class...>class Z, class, class...Ts>
  struct can_apply:std::false_type{};
  template<template<class...>class Z, class...Ts>
  struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;

can_apply< template, args... > 回答了问题 "is applying the args... to the template valid?"。这非常有用。

因为我们在 C++11 领域,enable_if_t 别名使代码看起来更漂亮:

template<bool b, class T=void>
using enable_if_t=typename std::enable_if<b,T>::type;

我们现在可以开始处理 result_of_t 别名:

namespace details {
  template<class Sig,class=void>
  struct result_of {};
  template<class F, class...Args>
  struct result_of<
    F(Args...),
    enable_if_t<can_apply<invoke_result, F, Args...>>
  > {
    using type=invoke_result<F,Args...>;
  };
}
template<class Sig>
using result_of_t = typename details::result_of<Sig>::type;

我们完成了。

这会生成一个名为 result_of_t 的 SFINAE 友好类型别名,它在任何兼容的 C++11 编译器中的工作方式类似于高质量的 C++14 std::result_of_t。它在 MSVC 上不能很好地工作,但那是因为 MSVC2015 仍然没有充分实现 C++11。

您的所有示例都应使用上述 result_of_t 替换 Result_of,否则将失败,因为它们无效。

int ff(int){return 2;}
typedef bool(*PF)(int);
auto fx = [](char ch){return tolower(ch);};

result_of_t<decltype(&ff)()> r1 = 7;

这会失败,因为您必须将 int 传递给 ff。这会起作用:

result_of_t<decltype(&ff)(int)> r1 = 7;

result_of_t<PF(int)> r2 = 2;

这会将 2 分配给 bool。所以它......有效。

result_of_t<decltype(fx)(char)> r5 = "a";

这会将 "a" 分配给 int,这可能不是您想要的。 (tolower returns C/C++ 中的 int

我们可以通过以下方式解决此问题:

auto fx = [](char ch)->char{return tolower(ch);};
result_of_t<decltype(fx)(char)> r5 = 'a';