无法推断出作为函数的模板参数

onq*_*tam 6 c++ templates overloading type-traits c++11

为什么不能F推断proxy()

它应该是可能的,因为我限制它 - 仅适用于返回的函数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);
}
Run Code Online (Sandbox Code Playgroud)

这是错误:

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'
Run Code Online (Sandbox Code Playgroud)

vso*_*tco 4

问题是这样的:

proxy(foo, 5);
Run Code Online (Sandbox Code Playgroud)

编译器尝试推断 的类型foo,但有 2 个重载。当然,它可以Args...从推导5,但 的类型foo仍然是不可推导的,因为编译器在进行类型推导时不知道选择哪个重载。

F请注意,编译器需要知道函数签名中的类型,即此处,因此 SFINAE 发挥了它的魔力:

is_same<
    typename result_of<F(Args...)>::type,
    int
>::value,
Run Code Online (Sandbox Code Playgroud)

F它绝对没有办法从调用中正确推断出 的类型proxy(foo, 5),因此 SFINAE 无法启动。作为旁注,请注意 C++ 不能仅根据返回类型进行重载。因此,您无法仅根据返回类型来区分两个具有相同名称的函数。您需要以某种方式强制参数匹配,这将 SFINAE 排除非候选重载。

某种程度上相关:推导独立函数的返回类型

以及标准中的相关引用,强调我的(感谢@TC 指出):

14.8.2.1 从函数调用推导模板参数 [temp.deduct.call]/(6.2)

(6) 当 P 为函数类型、函数类型指针或成员函数类型指针时:

  • (6.1) 如果参数是包含一个或多个函数模板的重载集,则该参数将被视为非推导上下文。

  • (6.2) 如果参数是重载集(不包含函数模板),则尝试使用该集的每个成员进行试验参数推导。如果仅重载集成员之一的推导成功,则该成员将用作推导的参数值。如果对重载集的多个成员推导成功,则该参数将被视为非推导上下文

  • @vsoftco [temp.deduct.call]/p6。 (2认同)