我能得到的所有编译器都同意这很好:
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对此非常满意。
哪种编译器是对的?
参考标准偏好的各个部分。
答案 0 :(得分:4)
模板函数的部分排序如何描述 在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
;扣除都失败了,所以都没有 模板被认为比其他模板更专业 函数调用含糊不清。
core issue 214的解决方案,[temp.deduct.partial]/11引入{{3}}:
在大多数情况下,所有模板参数必须具有值才能使演绎成功,但是对于部分排序目的,模板参数可以保持不带值,前提是它不用于用于部分排序的类型< /强>
显然,一旦打包,GCC对这一措辞的实施就会出现问题。
答案 1 :(得分:0)
恕我直言,我认为海湾合作委员会错了,CLANG在这里是正确的。我将尝试在下面证明我的主张:
根据标准§14.8.3/ p1重载决议[temp.over](强调我的):
函数模板可以通过(非模板)重载 其名称或其他功能模板的功能 名称。当写入对该名称的调用时(显式或隐式) 使用运算符表示法),模板参数推导(14.8.2) 并检查任何显式模板参数(14.3) 为每个函数模板找到模板参数值(如果 any)可以与该函数模板一起使用来实例化a 可以通过调用调用的函数模板特化 参数。对于每个函数模板,如果参数推论和 检查成功,模板参数(推断和/或显式) 用于合成单个函数模板的声明 添加到候选函数中的特化 用于重载解析。如果,对于给定的函数模板, 参数推导失败或合成的函数模板 专业化将是不正确的,没有这样的功能被添加到 该模板的候选函数集。完整的一套 候选函数包括所有合成声明和所有 非模板重载函数的同名。该 合成声明被视为与...中的任何其他函数一样 超出分辨率的剩余部分,除非明确指出 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指定了a 非模板函数将优先于模板 如果两个功能在其他方面同样好,则专业化 过度匹配的候选人。
从上面我们得到了显式模板参数将被检查,如果检查成功,那么将用于合成将被添加到候选函数以进行重载解析的特化。因此,您明确指定X
的事实与该过程无关。
同样来自C ++标准§13.3.3/ p1.7最佳可行功能[over.match.best] :
F1
和F2
是函数模板特化和函数F1
的模板比F2
的模板更专业 根据14.5.6.2中描述的偏序规则。
现在从§14.5.6.2/ p3函数模板的部分排序[temp.func.order] 我们得到的是,在部分排序参数包中也可以使用,所以这里也没问题。 / p>
现在:
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错误。