Tom*_*mis 4 c++ language-lawyer template-argument-deduction
我有一个模板函数,应该使用函数指针和参数,然后使用给定的参数调用函数指针(让我们称之为Invoke)。但是,当我使用重载函数作为参数调用模板函数时,模板推导失败。
我使用过enable_if,以便只有一个重载有效,但这没有帮助。
#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;
}
Run Code Online (Sandbox Code Playgroud)
尝试使用ideone。
mismatched argument pack lengths while expanding ‘std::is_convertible<Args, Params>::value’当两个重载都存在时,编译器会抱怨。当我注释掉int重载时,程序就可以正常编译,而当我注释掉std::string重载时,推论失败了,因为const char[]它不能隐式转换为int。
该标准(C ++ 17标准的17.8.2.1.6.2节)规定:
如果参数是重载集合(不包含函数模板),则尝试使用该集合的每个成员来推导试验参数。如果仅对重载集合成员之一进行推导成功,则将该成员用作推导的参数值。如果推论对于过载集的不止一个成员成功,则将参数视为非推论上下文。
因此,我希望编译器将尝试int重载,而推演将失败。当它尝试std::string超载时,扣除将成功。
由于推论仅对其中一个重载集合成员成功,因此我希望它会像int不存在重载一样继续进行,并且编译将像注释另一个重载一样成功进行,但是失败。
我哪里错了?
对标准的引用将不胜感激。
的&foo是不是一个函数指针,但过载组。您必须明确:
Invoke(static_cast<void(*)(std::string, std::string)>(&foo), "a", "b");
Run Code Online (Sandbox Code Playgroud)
为了简化失败enable_if,您可以采用带有可变参数包装的未指定函数指针类型,然后检查is_invocable:https :
//en.cppreference.com/w/cpp/types/is_invocable
这里的问题是有多个功能在起作用。推论Params...将在您进入模板的SFINAE部分之前进行。当它尝试从中推导Params..时void (*fn)(Params...),匹配void foo(int, int)和void foo(std::string, std::string)。由于找到多个匹配项,因此17.8.2.1.6.2声明将其视为非推论上下文。
由于它不能推导类型,因此会出现硬停止错误。SFINAE仅在模板参数推导步骤之后发生,在这种情况下无法执行。