不可用函数模板的类型演绎

Type deduction for non-viable function templates

本文关键字:类型 演绎 函数模板      更新时间:2023-10-16

在他对这个问题的回答和评论部分,Johannes Schaub说,当试图对一个函数模板进行模板类型推断时,需要比传递的参数更多的参数时,会出现"匹配错误":

template<class T>
void foo(T, int);
foo(42); // the template specialization foo<int>(int, int) is not viable

在另一个问题的上下文中,相关的是函数模板的类型推导是否成功(并且进行了替换):

template<class T>
struct has_no_nested_type {};
// I think you need some specialization for which the following class template
// `non_immediate_context` can be instantiated, otherwise the program is
// ill-formed, NDR
template<>
struct has_no_nested_type<double>
{ using type = double; };
// make the error appear NOT in the immediate context
template<class T>
struct non_immediate_context
{
    using type = typename has_no_nested_type<T>::type;
};

template<class T>
typename non_immediate_context<T>::type
foo(T, int) { return {}; }
template<class T>
bool foo(T) { return {}; }

int main()
{
    foo(42);      // well-formed? clang++3.5 and g++4.8.2 accept it
    foo<int>(42); // well-formed? clang++3.5 accepts it, but not g++4.8.2
}

当为T == int实例化第一个函数模板foo时,替换会产生一个不在foo直接上下文中的无效类型。这会导致一个硬错误(这就是相关问题的内容)

然而,当让foo推导其模板参数时,g++和clang++同意不发生实例化。正如Johannes Schaub解释的那样,这是因为存在"匹配错误"。

问题:什么是"匹配错误",在标准中是在哪里以及如何指定的?

可选问题:为什么g++的foo(42)foo<int>(42)之间存在差异?


到目前为止我发现/尝试了什么:

[over.match。/7和[temp.over]似乎描述了函数模板的重载解析细节。后者似乎强制要求用模板参数替换foo

有趣的是,[over.match。/7在检查函数模板(专门化)的可行性之前触发[temp.over] 中描述的过程。类似地,类型演绎不考虑,比如说,默认函数参数(除了使它们成为非演绎的上下文)。据我所知,这似乎与生存能力无关。

另一个可能重要的方面是如何指定类型演绎。它作用于单个函数参数,但我不明白包含/依赖于模板参数的参数类型(如T const&)和不依赖于模板参数的参数类型(如int)之间的区别在哪里。

然而,g++在显式指定模板形参(硬错误)和让它们被推导(推导失败/SFINAE)之间做出了区别。为什么?

我总结的是14.8.2.1p1

所描述的过程

模板实参推导是通过将每个函数模板形参类型(称其为p)与调用的相应实参类型(称其为A)进行比较来完成的,如下所述。

在我们的例子中,对于p我们有(T, int),对于A我们有(int)。对于第一对P/A,即Tint,我们可以将Tint匹配(通过14.8.2.5中描述的过程)。但对于第二"对",我们有int,但没有对应的。因此,不能对这个"对"进行演绎。

因此,根据14.8.2.5p2,"如果不能对任何p/A对进行类型演绎,…、模板参数演绎失败".

这样你就不用在函数模板中替换模板参数了。

这些可能都可以在标准(IMO)中更精确地描述,但我相信这是如何实现与Clang和GCC的实际行为相匹配的东西,这似乎是对标准的合理解释。