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

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不存在重载一样继续进行,并且编译将像注释另一个重载一样成功进行,但是失败。

我哪里错了?

对标准的引用将不胜感激。

ere*_*non 5

&foo是不是一个函数指针,但过载组。您必须明确:

Invoke(static_cast<void(*)(std::string, std::string)>(&foo), "a", "b");
Run Code Online (Sandbox Code Playgroud)

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


Nat*_*ica 5

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

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