Cannot deduce template argument that is a function

2020-07-11 08:03发布

Why cannot F be deduced for proxy()?

It should be possible because I am restricting it - only for functions that return an int.

#include <utility>
#include <iostream>
#include <type_traits>
using namespace std;

int foo(int bar) {
    cout << "int" << endl;
    return 2;
}

float foo(float bar) {
    cout << "float" << endl;
    return 1;
}

template <typename F, typename... Args>
typename enable_if<
    is_same<
        typename result_of<F(Args...)>::type,
        int
        >::value,
    typename result_of<F(Args...)>::type
    >::type
proxy(F func, Args&&... args) {
    return func(forward<Args>(args)...);
}

int main() {
    proxy(foo, 5);
}

Here is the error:

b.cpp:29:17: error: no matching function for call to 'proxy(<unresolved overloaded function type>, int)'
b.cpp:24:1: note:   template argument deduction/substitution failed:
b.cpp:29:17: note:   couldn't deduce template parameter 'F'

2条回答
Emotional °昔
2楼-- · 2020-07-11 08:42

The problem is this:

proxy(foo, 5);

The compiler tries to deduce the type of foo, but there are 2 overloads. Of course, it can deduce Args... from 5, but the type of foo is still non-deducible, since the compiler doesn't know which overload to pick when doing the type deduction.

Note that the compiler needs to know the type of F in the signature of the function, i.e. here, so SFINAE do its magic:

is_same<
    typename result_of<F(Args...)>::type,
    int
>::value,

There is absolutely no way for it to correctly deduce the type of F from the proxy(foo, 5) call, so SFINAE cannot kick in. As a side note, remark that C++ cannot overload based on the return type only. So you won't be able to differentiate two functions with the same name based on the return type alone. You'll need somehow to force a parameter matching which will SFINAE out the non-candidate overloads.

Somehow related: Deducing the return type of a standalone function

And a relevant quote from the standard, emphasize mine (thanks to @T.C. for pointing it out):

14.8.2.1 Deducing template arguments from a function call [temp.deduct.call]/(6.2)

(6) When P is a function type, pointer to function type, or pointer to member function type:

  • (6.1) If the argument is an overload set containing one or more function templates, the parameter is treated as a non-deduced context.

  • (6.2) If the argument is an overload set (not containing function templates), trial argument deduction is attempted using each of the members of the set. If deduction succeeds for only one of the overload set members, that member is used as the argument value for the deduction. If deduction succeeds for more than one member of the overload set the parameter is treated as a non-deduced context.

查看更多
Viruses.
3楼-- · 2020-07-11 08:42

In your example foo names a set of overloaded functions, and template argument deduction is unable to select one overload over another because they're both matches of equal rank.

Your SFINAE constraint check doesn't kick in until after F has been deduced, so it's of no help in dropping float foo(float) from the overload resolution set.

Say you renamed the function returning float to foof, then your example would compile. But if you tried calling proxy with foof as the function argument, the code would again fail to compile, this time because of the enable_if constraint.

To get your example to compile in its current state you must disambiguate which foo you are passing to proxy

proxy(static_cast<int(*)(int)>(foo), 5);
查看更多
登录 后发表回答