1

I was trying a program below:

#include<type_traits>
using namespace std;
template <class F, class R = typename result_of<F()>::type>
R call(F& f) { return f(); }
int answer() { return 42; }

int main()
{
    call(answer); 
    return 0;
}

"call(answer)" fails to compile

VC says 'R call(F&)' could not deduce template argument for 'R'

GCC says |note: template argument deduction/substitution failed:|error: function returning a function

I'm not sure if a "function name" could be used for templates. Where did I get wrong, how to make my call(answer) work?

Troskyvs
  • 7,537
  • 7
  • 47
  • 115

3 Answers3

1

You are calling f as an lvalue, so:

template <class F, class R = typename result_of<F&()>::type>
//                                               ^
R call(F& f) { return f(); }
T.C.
  • 133,968
  • 17
  • 288
  • 421
1

You can use forwarding references in these cases:

#include<type_traits>
#include<utility>
#include<cassert>

using namespace std;

template <class F, class R = typename result_of<F()>::type>
R call(F&& f) { return std::forward<F>(f)(); }

int answer() { return 42; }

int main()
{
    assert(call(answer) == 42);
    return 0;
}

It usually avoids troubles.

That said, why your code doesn't work is nicely explained by @T.C. in his answer.
See also the comments to this question for further details.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • But why I cannot call f as an lvalue? function name is always not an "lvalue"? – Troskyvs Jun 29 '16 at 06:16
  • You can call it as an lvalue. In your example, because of how template type deduction works, `F` is a function type, not a reference of it. Using forwarding references, `F` is deduced as a reference to `answer`. Try adding `static_assert(std::is_reference::value, "!");` within `call` in my example and in the @T.C.'s one. – skypjack Jun 29 '16 at 06:24
  • Another test would be adding `std::function func;` within `call` in both the examples. This should help you to understand what's happening there with type deduction for `F`. – skypjack Jun 29 '16 at 06:27
0

I suppose you could avoid the second template argument and use a combination of auto and decltype().

Something like

#include<type_traits>

using namespace std;

template <class F>
auto call(F& f) -> decltype( f() )
 { return f(); } 

int answer()
 { return 42; }

int main()
{
    call(answer); 

    return 0;
}

If you (when you) can use C++14, you can use simply auto

template <class F>
auto call(F& f)
 { return f(); } 

p.s.: sorry for my bad English.

max66
  • 65,235
  • 10
  • 71
  • 111