恕我直言,我相信 GCC 是错误的,而 CLANG 在这里是正确的。我将尝试在下面证明我的主张:
根据标准§14.8.3/p1过载分辨率[temp.over](Emphasis Mine):
函数模板可以被其名称的(非模板)函数或同名的(其他)函数模板重载。当编写对该名称的调用时(显式或隐式使用运算符符号),为每个函数模板执行模板参数推导 (14.8.2) 和任何显式模板参数检查 (14.3),以找到可与该函数模板一起使用的模板参数值(如果有),以实例化可以使用调用参数调用。对于每个函数模板,如果参数推导和检查成功,则模板参数(推导和/或显式)用于合成单个函数模板特化的声明,该特化添加到候选函数集以用于重载决策.如果对于给定的函数模板,参数推导失败或合成的函数模板特化格式不正确,则不会将此类函数添加到该模板的候选函数集中。完整的候选函数集包括所有综合声明和所有同名的非模板重载函数。除了 13.3.3 中明确指出的情况外,在重载决议的其余部分中,综合声明被视为与任何其他函数一样。144
[例子:
template<class T> T max(T a, T b) { return a>b?a:b; }
void f(int a, int b, char c, char d) {
int m1 = max(a,b); // max(int a, int b)
char m2 = max(c,d); // max(char a, char b)
int m3 = max(a,c); // error: cannot generate max(int,char)
}
144) 函数模板特化的参数不包含模板参数类型。推导参数上允许的转换集是有限的,因为参数推导过程会生成带有参数的函数模板,这些参数要么完全匹配调用参数,要么仅在可以通过允许的有限转换桥接的方式上有所不同。非推导参数允许全范围的转换。另请注意,13.3.3 指定非模板函数将优先于模板特化,如果这两个函数在其他方面同样适合重载匹配。
从上面我们可以看出,显式模板参数将被检查,如果检查成功,则将用于合成一个特化,该特化将添加到候选函数中以进行重载解析。因此,您明确指定的事实X
与该过程无关。
同样来自 C++ 标准§13.3.3/p1.7 最佳可行函数 [over.match.best]:
F1
和F2
是函数模板特化,
根据 14.5.6.2 中描述的部分排序规则,函数模板 forF1
比模板 for 更特化。F2
现在从§14.5.6.2/p3 函数模板的部分排序 [temp.func.order]我们得到,在部分排序参数包中也起作用,所以这里也没有问题。
现在:
template <typename X, typename... T>
auto bar(int, T...) -> void;
比:
template <typename X, typename Check, typename... T>
auto bar(Check, T...) -> void;
因此调用:
bar<void>(7, "");
不是模棱两可的。
基于上述,我认为这是一个 GCC 错误。