9

我可以得到的所有编译器都同意这很好:

template <typename Check, typename... T>
auto foo(Check, T...) -> void;

template <typename... T>
auto foo(int, T...) -> void;

int main()
{
  foo(7, "");
}

但是,根据 gcc,以下代码(带有无法从函数参数推导出的前导模板参数)是模棱两可的:

template <typename X, typename Check, typename... T>
auto bar(Check, T...) -> void;

template <typename X, typename... T>
auto bar(int, T...) -> void;

int main()
{
  bar<void>(7, ""); // ambiguous according to gcc
  bar<void>(7);     // just fine
}

另一方面,clang、msvc 和 icc 对此非常满意。

哪个编译器是对的?

优先参考标准的相应部分。

4

2 回答 2

4

这是核心问题 200

14.5.6.2 [temp.func.order] 第 3-5 段中对如何确定模板函数的部分排序的描述没有对非推导的模板参数做出任何规定。例如,以下代码中的函数调用是模棱两可的,尽管一个模板“显然”比另一个模板更专业:

template <class T> T f(int);
template <class T, class U> T f(U);
void g() {
    f<int>(1);
}

原因是两个函数参数列表都不允许T推导模板参数;两个推论都失败了,因此没有一个模板被认为比另一个模板更专业,并且函数调用是模棱两可的。

核心问题 214的解决方案,这个问题被简化为,引入了[temp.deduct.partial]/11

在大多数情况下,所有模板参数都必须具有值才能成功进行推导,但出于偏序目的,模板参数可能会保持没有值,前提是它未用于偏序的类型

显然,一旦包开始发挥作用,GCC 对这个措辞的实施就会出现问题。

于 2016-09-02T15:24:25.813 回答
0

恕我直言,我相信 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]

F1F2是函数模板特化, 根据 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 错误。

于 2016-09-02T08:22:53.900 回答