具有显式模板参数列表和 [temp.arg.explicit]/3 的函数调用推导失败

Deduction failure of function call with explicit template argument list and [temp.arg.explicit]/3

[temp.arg.explicit]/3 of the C++17 standard (final draft) 说关于使用明确指定的模板参数列表推导函数模板参数:

In contexts where deduction is done and fails, or [...], if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.

这如何应用于参数包?

考虑

template<typename...>
struct S {
    S(int) {}
};

template<typename... A>
void f(S<A...>) {}

int main() {
    f<int>(0);
}

这可以在 MSVC 上编译,但不能在 GCC 和 Clang 上编译,请参阅 godbolt。我的直觉也是它应该失败,因为推导会失败,但上面的引用似乎暗示即使推导失败,因为 f<int> (在我的理解中)唯一标识模板专业化, f<int> 应该被认为是引用该专业化然后调用它,没有重载决议,这将起作用,隐式地将 0 转换为 S<int>.

我对引用的理解有什么问题,或者 MSVC 确实正确吗?


请注意,如果我们尝试调用 f<>(0);(根据上述考虑,我认为这应该可行)所有三个编译器都拒绝编译。

有一个与此案例密切相关的开放核心语言问题 (issue 2055: Explicitly-specified non-deduced parameter packs)。

根据我对这个问题中隐含陈述的理解,其意图是编译器应该像 MSVC 一样运行,但据说标准不够明确。

与该问题相关的还有 [temp.arg.explicit]/6,它告诉我们函数参数的隐式转换(如您所愿)是

if the parameter type contains no template-parameters that participate in template argument deduction. [ Note: Template parameters do not participate in template argument deduction if they are explicitly specified. [...] ]

所以,现在的问题是A...是否参与模板参数推导。 (此时我想指出,如果我们用一个模板参数替换参数包,OPs 代码也会在 gcc/clang 下编译,因为它应该是明确指定的)。

有人可能会争辩说 A... 是明确指定的,因此不参与推导。但我认为有人错了。 [temp.arg.explicit]/9 告诉我们演绎可以扩展明确指定的模板参数列表。因此,f<int>(S<int, char>{0}); 是有效的并且 A... 被推导为 int, char。所以这种情况下A...肯定是参与推演的。但由于此调用仅在参数上与您的调用不同,因此也必须在您的调用中进行推导。

换句话说,f<int>(0); 也可能意味着调用 f<int, char>,因此,它不指定单个函数模板规范。

这无关紧要。没有“没有重载决议”的函数调用之类的东西。 CWG2092 说明了这一点。

[temp.over]/1 控件(为了便于阅读而分开;强调我的):

When a call to the name of a function or function template is written (explicitly, or implicitly using the operator notation), template argument deduction ([temp.deduct]) and checking of any explicit template arguments ([temp.arg]) 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 specialization 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 synthesize the declaration of a single function template specialization 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 synthesized function template specialization 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 synthesized declarations and all of the non-template overloaded functions of the same name. The synthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in [over.match.best].