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

时间:2015-06-03 15:06:04

标签: c++ templates c++11 overloading typetraits

为什么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);
}

这是错误:

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 个答案:

答案 0 :(得分:4)

问题在于:

proxy(foo, 5);

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

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

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

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

以某种方式相关:Deducing the return type of a standalone function

标准的相关引用,强调我的(感谢@ T.C。指出):

14.8.2.1从函数调用中减去模板参数[temp.deduct.call] /(6.2)

  

(6)当P是函数类型时,指向函数类型的指针或指向成员函数的指针类型:

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

  •   
  • (6.2)如果参数是重载集(不包含函数)   模板),尝试使用每个的试验推论   成员。如果扣除成功只有一个过载   设置成员,该成员用作参数值   扣除。 如果多个成员的扣除成功   过载设置参数被视为非推导上下文

  •   

答案 1 :(得分:2)

在您的示例中,foo命名一组重载函数,模板参数推导无法选择一个重载而不是另一个重载,因为它们都是相同等级的匹配。

您的SFINAE约束检查在推断F之后才会启动,因此从重载决策集中删除float foo(float)没有任何帮助。

假设您将函数重命名为float并返回foof,那么您的示例将进行编译。但是,如果您尝试使用proxy作为函数参数调用foof,则代码将再次无法编译,这次是因为enable_if约束。

要让您的示例以当前状态进行编译,您必须消除您传递给foo的{​​{1}}

的歧义。
proxy