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

Function overloading: empty parameter list vs parameter pack

本文关键字:参数 列表 重载 函数      更新时间:2023-10-16
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;
}

当我像这样调用函数时

call(10);

GCC、Clang 和 MSVC 都使用 #1。

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

如果对应于 Pi的参数声明是函数参数包,则将其声明符 id的类型与 A 的参数类型列表中的每个剩余参数类型进行比较。每次比较都会推导出由函数参数包扩展的模板参数包中后续位置的模板参数。在部分排序期间,如果 Ai最初是一个函数参数包:

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

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

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

然后从 #1 推导出 #2,T为 A,T, Args...为 P,也成功地产生了T = T, Args... = {}

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

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


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

使用得到的类型PA,推导则为 在 [温度扣除类型] 中描述。如果P是函数参数包, 参数模板的每个剩余参数类型的类型A与函数的声明符 ID的类型P进行比较 参数包。每个比较都推导出模板参数 模板参数包中的后续位置由 函数参数包。同样,如果A从 函数参数包,与剩余的每个参数进行比较 参数模板的类型。如果给定的扣除成功 类型,参数模板中的类型至少被视为 与参数模板中的类型一样专用。

当参数模板#1且参数模板#2时,#2中的声明符 idArgs(这是一种类型(将与#1中剩余的每个参数进行比较(没有(。 因此,#2至少与#1一样专业。

#2参数模板并且参数模板#1时,#2中的声明符 idArgs(这是一种类型(将与#1中剩余的每个参数进行比较(没有(。 因此,#1至少和#2一样专业。

似乎模棱两可,对吧? 现在我们有了决胜局 [temp.deduct.partial]/11:

如果在考虑上述因素后,函数模板F至少为 专门用作函数模板G,反之亦然,如果G具有F没有相应参数包的尾随参数包 参数,如果F没有尾随参数包,则FG更专业.

显然,#1#2更专业.