模板演绎中的部分排序过程是什么?< / h1 >

What is the partial ordering procedure in template deduction

本文关键字:lt gt h1 是什么 过程 演绎 排序      更新时间:2023-10-16

阅读c++ 11标准我不能完全理解下面语句的含义。

使用两组类型来确定偏序。为每一个所涉及的模板中有原始函数类型和转换函数类型。[注:转换类型的创建在14.5.6.2中描述。- end note]扣款过程使用的原始类型和转换后的类型作为参数模板其他模板作为参数模板。这个过程要进行两次对于部分排序比较中涉及的每个类型:一次使用转换后的模板1作为参数模板,模板2作为参数模板参数模板,并再次使用转换后的模板-2作为参数template和template-1作为形参template
——N3242 14.8.2.4.2

虽然Xeo在评论中给出了很好的描述,但我将尝试通过一个工作示例逐步解释。

首先,你引用的这段话的第一句话是:

对于所涉及的每个模板,都有原始函数类型和转换的函数类型。[…]

等等,这个">转换后的函数类型"是什么?第14.5.6.2/3段解释:

生成转换后的模板,对于每个类型、非类型或模板模板参数(包括模板参数包(14.5.3)分别合成唯一的类型、值或类模板并将其替换为模板函数类型中该形参的每次出现[…]

这种形式的描述可能听起来晦涩难懂,但实际上非常简单。让我们以这个函数模板为例:

template<typename T, typename U>
void foo(T, U) // #1

现在,因为TU是类型参数,上面的段落要求我们为T(无论什么)选择一个相应的类型参数,并在T出现的函数签名中替换它,然后对U做同样的事情。

现在">合成唯一类型"意味着您必须选择一个在其他地方没有使用过的虚构类型,我们可以将其称为P1(然后为U选择P2),但这会使我们的讨论变得毫无意义。

让我们简化一下,为T选择int,为U选择bool——我们不会在其他地方使用这些类型,所以就我们的目的而言,它们和P1P2一样好。

变换后,我们得到:

void foo(int, bool) // #1b

这是原始foo()函数模板的转换函数类型。

那么让我们继续解释你引用的段落。第二句说:

演绎过程使用转换后的类型作为实参模板和另一个模板的原始类型作为参数模板。[…]

等等,什么">other template"?到目前为止,我们只有一个foo()过载。是的,但是为了在函数模板之间建立一个顺序,我们至少需要其中的两个,所以我们最好创建第二个。让我们用:

template<typename T>
void foo(T const*, X<T>) // #2

其中X是我们的某个类模板。

那么第二个函数模板是什么呢?啊,是的,我们需要做与之前对foo()的第一次重载所做的相同的事情并对其进行转换:所以,再次,让我们为T选择一些类型参数并替换T。这次我将选择char(在这个例子中我们没有在其他地方使用它,所以它和一些虚构的P3一样好):

void foo(char const*, X<char>) #2b

很好,现在他有两个函数模板和相应的转换函数类型。那么如何判断#1是否比#2更专门化呢?

从上面的句子中我们知道,原始模板和它们转换后的函数类型必须以某种方式匹配。但如何?这就是第三句话的解释:

对于参与偏序比较的每种类型,这个过程都要进行两次:第一次使用转换后的模板-1作为实参模板,模板-2作为形参模板;第二次使用转换后的模板-2作为实参模板,模板-1作为形参模板

因此,基本上第一个模板(#1b)的转换后的函数类型将与原始第二个模板(#2)的函数类型进行匹配。当然反过来,第二个模板(#2b)的转换后的函数类型将与原始第一个模板(#1)的函数类型进行匹配。

如果在一个方向上匹配成功,而在另一个方向上不匹配,那么我们就知道其中一个模板比另一个更专门化。否则,两者都不是更专门化的。

让我们开始。首先,我们必须匹配:

void foo(int, bool) // #1b

反对:

template<typename T>
void foo(T const*, X<T>) // #2

是否有一种方法可以对T执行类型演绎,使T const*恰好成为int,X<T>恰好成为bool?(实际上,一个精确的匹配是没有必要的,但是这个规则确实有一些例外,它们与说明部分排序机制的目的无关,所以我们将忽略它们)。

几乎没有。我们来试试反过来匹配。我们应该匹配:

void foo(char const*, X<char>) // #2b

反对:

template<typename T, typename U>
void foo(T, U) // #1

我们可以推导出TU来分别生成char const*X<char>的精确匹配吗?当然!这是微不足道的。我们只选T = char const*U = X<char>

我们发现第一次重载的foo()(#1b)转换后的函数类型不能与第二次重载的foo()(#2)的原始函数模板匹配;另一方面,第二个重载(#2b)转换后的函数类型可以与第一个重载(#1)的原始函数模板匹配。

结论?foo()的第二个重载比第一个更专门化。

选择一个反例,考虑以下两个函数模板:
template<typename T, typename U>
void bar(X<T>, U)
template<typename T, typename U>
void bar(U, T const*)

哪个重载比另一个更专门化?我不会再重复整个过程,但是您可以这样做,并且这应该使您确信,在任何一个方向上都不能产生匹配,因为第一个重载比第二个重载在与第一个参数有关的方面更专门化,但第二个重载在与第二个参数有关的方面比第一个重载更专门化。

结论?没有一个函数模板比另一个更专门化。

在这篇解释中,我忽略了很多细节、规则的例外和标准中的晦涩段落,但你引用的段落中概述的机制确实是这样的。

还要注意,上面概述的相同机制用于在模板的部分专门化之间建立">更专门化"排序,方法是首先为每个专门化创建一个关联的虚拟函数模板,然后通过本答案中描述的算法对这些函数模板排序。

c++ 11标准第14.5.5.2/1段规定:

对于两个类模板部分专门化,第一个至少与第二个if一样专门化,给定在重写为两个函数模板之后,第一个函数模板至少与第二个函数模板一样专门化根据函数模板的排序规则(14.5.6.2):

-第一个函数模板具有与第一个部分专门化相同的模板参数,并且具有单个函数形参,其类型是带有模板实参的类模板专门化第一部分专门化,和

-第二个函数模板具有与第二个部分特化相同的模板参数并且有一个函数形参,其类型是与模板相关的类模板专门化第二部分专门化的实参。

希望有帮助。