阅读C ++ 11标准我无法完全理解以下语句的含义。例子非常受欢迎。
两组类型用于确定部分排序。对于每一个 涉及的模板有原始的功能类型和 转换功能类型。 [注意:创建转换后的类型 在14.5.6.2中描述。 - 尾注]扣除过程使用 转换类型作为参数模板和原始类型 其他模板作为参数模板。这个过程完成了两次 对于部分排序比较中涉及的每种类型:一旦使用 转换后的模板-1作为参数模板,模板-2作为参数模板 参数模板,再次使用转换后的模板-2作为 参数模板和template-1作为参数模板
- N3242 14.8.2.4.2
答案 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
既然T
和U
是类型参数,上面的段落要求我们为T
(无论如何)选择相应的类型参数,并在函数签名中的任何地方替换它出现T
,然后对U
执行相同操作。
现在&#34; 合成一个独特的类型&#34;意味着您必须选择一个您尚未在其他任何地方使用过的虚构类型,我们可以调用P1
(然后为P2
选择U
),但这样做会使我们的讨论无用正式。
我们只是简化一些事情,为int
选择T
,为bool
选择U
- 我们不会在其他任何地方使用这些类型,所以就我们而言,它们与P1
和P2
一样好。
因此,在转型之后,我们有:
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
完全变为T
,T const*
变为int
? (实际上,完全匹配不是必需的,但是这个规则实际上很少有例外,它们与说明部分排序机制的目的无关,所以我们忽略它们)
几乎没有。因此,让我们尝试匹配相反的方式。我们应该匹配:
X<T>
反对:
bool
我们可以在此处推断void foo(char const*, X<char>) // #2b
和template<typename T, typename U>
void foo(T, U) // #1
分别为T
和U
生成完全匹配吗?当然!这是微不足道的。我们只选择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):
- 第一个函数模板具有与第一个部分特化相同的模板参数并具有 单个函数参数,其类型是具有模板参数的类模板特化 第一部分专业化,和
- 第二个功能模板具有与第二个部分特化相同的模板参数 并且有一个函数参数,其类型是模板的类模板特化 第二部分专业化的论据。
希望这会有所帮助。