gcc 与 clang、msvc 和 icc:这个函数调用是否有歧义?

gcc vs. clang, msvc and icc: Is this function call ambiguous?

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

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 对此非常满意。

哪个编译器是正确的?

首选标准的各个部分。

恕我直言,我认为 GCC 是错误的,而 CLANG 在这里是正确的。我将尝试在下面证明我的主张:

根据标准§14.8.3/p1 过载分辨率[temp.over](Emphasis Mine:

A function template can be overloaded either by (non-template) functions of its name or by (other) function templates of the same name. When a call to that name is written (explicitly, or implicitly using the operator notation), template argument deduction (14.8.2) and checking of any explicit template arguments (14.3) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialisation that can be invoked with the call arguments. For each function template, if the argument deduction and checking succeeds, the template-arguments (deduced and/or explicit) are used to synthesise the declaration of a single function template specialisation which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails or the synthesised function template specialisation would be ill-formed, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all the synthesised declarations and all of the non-template overloaded functions of the same name. The synthesised declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in 13.3.3.144

[Example:

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) The parameters of function template specializations contain no template parameter types. The set of conversions allowed on deduced arguments is limited, because the argument deduction process produces function templates with parameters that either match the call arguments exactly or differ only in ways that can be bridged by the allowed limited conversions. Non-deduced arguments allow the full range of conversions. Note also that 13.3.3 specifies that a non-template function will be given preference over a template specialisation if the two functions are otherwise equally good candidates for an overload match.

从上面我们了解到,将检查显式模板参数,如果检查成功,则将用于合成一个特化,该特化将添加到候选函数中以进行重载解析。因此,您明确指定 X 的事实与该过程无关。

同样来自 C++ 标准 §13.3.3/p1.7 最佳可行函数 [over.match.best]:

F1 and F2 are function template specialisations, and the function template for F1 is more specialised than the template for F2 according to the partial ordering rules described in 14.5.6.2.

现在从 §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 错误。

这是core issue 200

The description of how the partial ordering of template functions is determined in 14.5.6.2 [temp.func.order] paragraphs 3-5 does not make any provision for nondeduced template parameters. For example, the function call in the following code is ambiguous, even though one template is "obviously" more specialized than the other:

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

The reason is that neither function parameter list allows template parameter T to be deduced; both deductions fail, so neither template is considered more specialized than the other and the function call is ambiguous.

core issue 214, which this one was reduced to, introduced [temp.deduct.partial]/11的分辨率:

In most cases, all template parameters must have values in order for deduction to succeed, but for partial ordering purposes a template parameter may remain without a value provided it is not used in the types being used for partial ordering.

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