【问题标题】:Type deduction for non-viable function templates不可行功能模板的类型推导
【发布时间】:2014-03-10 12:51:14
【问题描述】:

his answerthis question 和评论部分,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 直接上下文中的无效类型。这会导致一个硬错误(这就是the related question 的意义所在。)

然而,当让foo 推导出它的模板参数时,g++ 和 clang++ 同意没有实例化发生。如Johannes Schaub explains,这是因为存在“匹配错误”。

问题:什么是“匹配错误”,标准中在何处以及如何指定?

替代问题:为什么 foo(42)foo&lt;int&gt;(42) 对于 g++ 有区别?


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

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

有趣的是,[over.match.funcs]/7 触发了 [temp.over] before 中描述的过程,检查函数模板的可行性(特化)。 类似地,类型推导不考虑默认函数参数(除了使它们成为非推导上下文)。据我所知,它似乎并不关心可行性。

另一个可能重要的方面是如何指定类型推导。它作用于单个函数参数,但我看不出包含/依赖于模板参数的参数类型(如T const&amp;)和不依赖于模板参数的参数类型(如int)之间的区别。

然而,g++ 在显式指定模板参数(硬错误)和让它们被推断(推断失败/SFINAE)之间有所不同。为什么?

【问题讨论】:

    标签: c++ templates overload-resolution template-argument-deduction


    【解决方案1】:

    我总结的是 14.8.2.1p1 中描述的过程

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

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

    因此,根据 14.8.2.5p2,“如果无法对任何 P/A 对进行类型推导,...,模板 推论失败。”。

    您将永远无法将模板参数替换为函数模板。

    这可能都可以在标准 (IMO) 中更准确地描述,但我相信这是可以实现事物以匹配 Clang 和 GCC 的实际行为的方式,这似乎是对 Standardese 的合理解释。

    【讨论】:

    • 嗯,我一直想知道这些非依赖参数类型的处理是在哪里描述的。当显式指定所有模板参数时,不需要执行推导步骤,因此不会出现匹配错误。这听起来很合理,但让我想起了最初的 14.7.1p7 优化(尤其是 14.8.2p6 中规定的两个替换)。也许我只是迷失在标准语中;但我不知道在哪里允许省略这个扣除步骤(或“扣除”/匹配非依赖类型)。
    • 在参数推导之前(即在模式匹配过程之前),明确指定的参数被替换。之后,函数参数可能变得不相关。在某种程度上,对不再包含参数的参数的精确处理是 open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1391 的主题。编译器目前似乎在检查是否存在隐式转换作为推导过程的一部分(clang 确实接受第一个测试用例,但这可能是因为它在事实发生之前根据实际参数计数过滤掉了,显然)。
    • 没有文字表明不再包含参数的参数会从匹配中被忽略。 14.8.2.1p1 说通过比较每个函数模板参数类型。然而,整个规范并不是太“安全证明”:与 14.8.2.5p1 比较,它说“在每种情况下,都会比较 根据模板参数(称为 P)指定的类型具有实际类型”。但是,我认为这是某种“概述”,只是忽略了某些对实际上可能是非依赖的细节。它仍然会降低您对 IMO 文本的信任程度。
    • 如果你按照最后的建议“在扣除和替换之间”做,你会发现,就那里显示的第二个例子而言,B&lt;char&gt; 不能隐式转换为 @987654330 @ *在替换 T 之前。因此,您在引发实际错误之前拒绝模板。但是在您的情况下,您没有相应的论据开始。因此,在您的情况下,这将是参数推断期间的直接错误,因为您为其提供了一个不可行的 P/A“对”
    • 另请注意,模板参数的显式规范以及后面在 DR 中该示例的错误无法与您的进行比较,因为在 DR 示例中,会出现错误因为转换检查,而不是仅仅因为替换。在您的情况下,仅替换(以及以下:: 成员选择)就会导致类模板的实例化。
    猜你喜欢
    • 2017-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多