什么是模板推导中的部分排序程序

时间:2013-06-09 02:57:05

标签: c++ templates c++11 partial-ordering template-deduction

阅读C ++ 11标准我无法完全理解以下语句的含义。例子非常受欢迎。

  

两组类型用于确定部分排序。对于每一个   涉及的模板有原始的功能类型和   转换功能类型。 [注意:创建转换后的类型   在14.5.6.2中描述。 - 尾注]扣除过程使用   转换类型作为参数模板和原始类型   其他模板作为参数模板。这个过程完成了两次   对于部分排序比较中涉及的每种类型:一旦使用   转换后的模板-1作为参数模板,模板-2作为参数模板   参数模板,再次使用转换后的模板-2作为   参数模板和template-1作为参数模板
   - N3242 14.8.2.4.2

1 个答案:

答案 0 :(得分:53)

虽然Xeo给了a pretty good description in the comments,但我会尝试用一个有效的例子给出一步一步的解释。

首先,你引用的段落中的第一句话说:

  

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

坚持,这是什么" 转换后的功能类型"?第14.5.6.2/3段解释说:

  

为每种类型,非类型或模板模板参数生成转换后的模板(包括   模板参数包(14.5.3)分别合成唯一的类型,值或类模板   并将其替换为模板[...]

的函数类型中每次出现的参数

这种正式的描述可能听起来模糊不清,但实际上它实际上非常简单。我们以此函数模板为例:

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

既然TU是类型参数,上面的段落要求我们为T(无论如何)选择相应的类型参数,并在函数签名中的任何地方替换它出现T,然后对U执行相同操作。

现在&#34; 合成一个独特的类型&#34;意味着您必须选择一个您尚未在其他任何地方使用过的虚构类型,我们可以调用P1(然后为P2选择U),但这样做会使我们的讨论无用正式。

我们只是简化一些事情,为int选择T,为bool选择U - 我们不会在其他任何地方使用这些类型,所以就我们而言,它们与P1P2一样好。

因此,在转型之后,我们有:

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

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

因此,让我们继续解释您引用的段落。第二句话说:

  

演绎过程使用变换后的类型作为参数模板,将其他模板的原始类型用作参数模板。 [...]

等等,&#34; 其他模板&#34;?到目前为止,我们只有一个foo()的重载。是的,但为了在功能模板之间建立排序,我们至少需要其中两个,所以我们最好创建第二个。让我们使用:

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

X是我们的某个类模板。

现在第二个功能模板是什么?啊,是的,我们需要像以前那样对foo()的第一次重载做同样的事情并对其进行转换:再次,让我们为T选择一些类型参数并替换{{1无处不在。我这次会选择T(我们在此示例中的任何其他地方都没有使用它,因此它与某些虚构的char一样好):

P3

很好,现在他有两个函数模板和相应的转换函数类型。那么如何确定void foo(char const*, X<char>) #2b 是否比#1更专业化,反之亦然?

我们从上面的句子中得知,原始模板及其转换后的函数类型必须以某种方式匹配。但是怎么样?这就是第三句话的解释:

  

对于部分排序比较中涉及的每种类型,此过程完成两次:一次使用转换后的template-1作为参数模板,使用template-2作为参数模板,再次使用转换后的template-2作为参数模板, template-1作为参数模板

所以基本上第一个模板(#2)的转换函数类型将与原始第二个模板的函数类型匹配({{ 1}})。当然反过来说,第二个第二个模板(#1b)的转换函数类型首先与原始的函数类型匹配模板(#2)。

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

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

#2b

反对:

#1

我们是否可以在void foo(int, bool) // #1b 上执行类型扣除,以便template<typename T> void foo(T const*, X<T>) // #2 完全变为TT 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()的第二次重载的原始函数模板匹配({{ 1}});另一方面,第二次重载的转换函数类型(#1b可以与第一次重载(foo())的原始函数模板匹配。

结论? #2的第二个重载比第一个更加专业。

要选择一个反例,请考虑以下两个函数模板:

#2b

哪种超载比另一种更专业?我不会再次完成整个过程,但是你可以做到,这应该说服你不能在任何一个方向上产生匹配,因为第一次过载比第二次过载更专业参数,但第二个参数比第一个参数更专业。

结论?两种功能模板都不比另一种更专业。

现在在这个解释中,我忽略了很多细节,规则的例外以及标准中的神秘段落,但你引用的段落中概述的机制确实是这个。

另请注意,上面列出的相同机制用于建立&#34; 更专业化而非&#34;在模板的部分特化之间进行排序,首先为每个特化创建一个关联的虚构函数模板,然后通过本答案中描述的算法对这些函数模板进行排序。

这由C ++ 11标准的第14.5.5.2/1段规定:

  

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

     

- 第一个函数模板具有与第一个部分特化相同的模板参数并具有   单个函数参数,其类型是具有模板参数的类模板特化   第一部分专业化,和

     

- 第二个功能模板具有与第二个部分特化相同的模板参数   并且有一个函数参数,其类型是模板的类模板特化   第二部分专业化的论据。

希望这会有所帮助。