1
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";

When I compile with gcc, I get the following error:

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

Why am I getting this error and how do I fix it?

Barry
  • 286,269
  • 29
  • 621
  • 977
Milo Lu
  • 3,176
  • 3
  • 35
  • 46

2 Answers2

3

The short answer is:

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

will make your use cases "work". Some will fail to compile because you passed in the wrong signature, or the types don't match.

However it isn't the best way to do it.

C++11 compilers do not all implement the best-practices version of result_of: namely, a SFINAE friendly one. If you are writing a helper, I do it in best-practices style.

First, an alias to do decltype evaluation:

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

Next, some metaprogramming boilerplate I find useful:

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... > answers the question "is applying the args... to the template valid?". This is surprisingly useful.

As we are in C++11 land, an enable_if_t alias makes code look prettier:

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

We can now start working on a result_of_t alias:

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;

and we are done.

This generates a SFINAE friendly type alias called result_of_t that works like a high quality C++14 std::result_of_t in any compliant C++11 compiler. It won't work well on MSVC, but that is because MSVC2015 still doesn't implement C++11 sufficiently.

All of your examples should work with the above result_of_t replacing Result_of, or fail because they are invalid.

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

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

this fails, because you have to pass an int to ff. This would work:

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

result_of_t<PF(int)> r2 = 2;

this assigns 2 to a bool. So it ... works.

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

this assigns "a" to an int, which is probably not what you want. (tolower returns an int in C/C++).

We can fix this via:

auto fx = [](char ch)->char{return tolower(ch);};
result_of_t<decltype(fx)(char)> r5 = 'a';
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
2

There are several things wrong with this line:

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

First, Result_of takes a function type and a list of arguments, comma separated. You're not providing them that way. So it's interpreting F to be (int)(*)(int)(), which would be a pointer to a function taking an int returning a function returning an int. But it's illegal in C++ to have a function return a function, hence the error.

Second, ff takes an int, not nothing. Lastly, ff decays to a pointer-to-function and you need the actual function. The correct expression would be:

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

Similarly, the next need to be:

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
Barry
  • 286,269
  • 29
  • 621
  • 977