函数重载:空参数列表与参数包

jac*_*k X 5 c++ templates language-lawyer overload-resolution variadic-templates

template <typename T>
void call(T) {  //#1
    std::cout << "1" << std::endl;
}

template <typename T, typename...Args>
void call(T, Args...) {  //#2
    std::cout << "2" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

当我这样调用函数时

call(10);
Run Code Online (Sandbox Code Playgroud)

GCC,Clang和MSVC都使用#1。

但是,标准中的部分排序规则指出:

如果对应于P i的参数声明是一个函数参数包,则将其声明符ID 的类型与A 的parameter-type-list中的每个其余参数类型进行比较。每个比较都推导出模板参数用于A中的后续位置。模板参数包由功能参数包扩展。在部分订购期间,如果A i最初是一个函数参数包:

  • (10.1)如果P不包含对应于A i的函数参数类型,则A i被忽略;

  • (10.2)否则,如果P i不是函数参数包,则模板参数推导失败。

当我们从#2推论出#1时(T, Args...以A表示,T以P表示),P不包含与对应的模板参数Args...Args...被忽略,因此可以成功地从#2推导出#1。

然后,以TA和T, Args...P 从#1推论出#2的结果也成功T = T, Args... = {}

因此,根据局部排序规则,在调用时call(10),编译器应给出模棱两可的错误,但实际上所有编译器都称为#1,这是为什么呢?

L. *_* F. 3

编译器是正确的。 #1比 更专业#2


部分排序模板参数包的规则在[temp.deduct.partial]/8中指定:

使用结果类型PA,然后按照 [temp.deduct.type] 中的描述进行推导。如果P是函数参数包,A则将参数模板的每个剩余参数类型的类型与函数参数包的declarator-idP的类型进行比较。每次比较都会推导出由函数参数包扩展的模板参数包中后续位置的模板参数。类似地,如果是从函数参数包转换而来,则将其与参数模板的每个剩余参数类型进行比较。如果给定类型的推导成功,则认为参数模板中的类型至少与参数模板中的类型一样专业。A

当形参模板为#1且实参模板为 时#2, from的 declarator-id Args(它是一种类型)#2将与 in 中的每个剩余参数进行比较#1(没有)。因此,#2至少与 一样专业#1

当形参模板为#2且实参模板为 时#1, from的 declarator-id Args(它是一种类型)#2将与 in 中的每个剩余参数进行比较#1(没有)。因此,#1至少与 一样专业#2

看起来很暧昧,对吧?现在我们有了决胜局,[temp.deduct.partial]/11

考虑到上述情况后,如果函数模板F至少与函数模板一样专业化G,反之亦然,并且如果G有一个尾随参数包,但F没有对应的参数,并且如果F没有尾随参数包, F则为比 更专业G

显然,#1比 更专业#2