我有一个模板函数,应该带有一个函数指针和参数,然后使用给定的参数调用函数指针(我们将其称为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
重载一样继续进行,并且编译将像其他重载一样成功超载已被注释掉,但失败。
我在哪里错了?
请参考标准。
答案 0 :(得分:5)
&foo
不是函数指针,而是重载集。您必须明确:
Invoke(static_cast<void(*)(std::string, std::string)>(&foo), "a", "b");
为简化失败的enable_if
,您可以采用带有可变参量包装的未指定函数指针类型,并检查is_invocable
:
https://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仅在模板参数推导步骤之后发生,在这种情况下无法执行。