为什么模板参数推导在重载函数中失败?

时间:2019-06-06 19:02:19

标签: c++ language-lawyer template-deduction

我有一个模板函数,应该带有一个函数指针和参数,然后使用给定的参数调用函数指针(我们将其称为BETWEEN)。但是,当我使用重载函数作为参数调用模板函数时,模板推导失败。

我使用过enable_if,以便只有一个重载有效,但这无济于事。

Invoke

尝试ideone

当两个重载都存在时,编译器会抱怨#include <string> #include <type_traits> void foo(int, int){} void foo(std::string, std::string) {} template <bool Val1, bool Val2, bool ...Rest> struct And { enum {value = And<Val1 && Val2, Rest...>::value}; }; template <bool Val1, bool Val2> struct And<Val1, Val2> { enum {value = Val1 && Val2}; }; template <typename ...Params, typename ...Args, typename = typename std::enable_if< And<std::is_convertible<Args, Params>::value...>::value >::type> void Invoke(void (*fn)(Params...), Args ...args){} int main() { Invoke(&foo, "a", "b"); return 0; } 。 当我注释掉mismatched argument pack lengths while expanding ‘std::is_convertible<Args, Params>::value’重载时,程序就可以正常编译,当我注释掉int重载时,推论失败了,因为std::string不能隐式转换为{{ 1}}。

该标准(C ++ 17标准的17.8.2.1.6.2节)规定:

  

如果参数是重载集(不包含函数模板),则尝试参数推导为   尝试使用集合中的每个成员。如果仅对超载集合之一进行演绎成功   成员,该成员用作推论的参数值。如果扣除成功   比重载集合的一个成员多的参数被视为非推导上下文。

因此,我希望编译器会尝试const char[]重载,而推导将失败。当它尝试int重载时,推断将成功。

由于推论仅对其中一个重载集合成员成功,因此我希望它会像没有int重载一样继续进行,并且编译将像其他重载一样成功超载已被注释掉,但失败。

我在哪里错了?

请参考标准。

2 个答案:

答案 0 :(得分:5)

&foo不是函数指针,而是重载集。您必须明确:

Invoke(static_cast<void(*)(std::string, std::string)>(&foo), "a", "b");

为简化失败的enable_if,您可以采用带有可变参量包装的未指定函数指针类型,并检查is_invocablehttps://en.cppreference.com/w/cpp/types/is_invocable

答案 1 :(得分:5)

这里的问题是有多个功能在起作用。 Params...的推论将在您到达模板的SFINAE部分之前进行。当它试图从Params..推论void (*fn)(Params...)时,它匹配void foo(int, int)void foo(std::string, std::string)。由于找到了多个匹配项,因此17.8.2.1.6.2声明将其视为非推论上下文。

由于它不能推导类型,因此会出现硬停止错误。 SFINAE仅在模板参数推导步骤之后发生,在这种情况下无法执行。