部分排序背后的基本思想并不复杂。首先,让我们给一些
T
重新命名:
template <typename T>
void foo(T); // #1
template <typename U, int N>
void foo(array<U, N>); // #2
template <typename V>
void foo(array<V, 1>); // #3
这个想法是每个 array<U, N>
都是一个 T
,但并非每个 T
都是一个 array<U, N>
。这使得 #2 比 #1 更为专门化。
而且每个 array<V, 1>
都是一个 array<U, N>
,但并非每个 array<U, N>
都是一个 array<V, 1>
(因为 N
可能不是 1)。这使得 #3 比 #2 更为专门化。
为了检查每个
array<U, N>
是否是一个
T
,我们用一个“唯一类型”替换
U
,并用一个“唯一值”替换
N
。称这些为
UniqT1
和
uniqv1
。然后,我们得到类型
array<UniqT1, uniqv1>
并尝试从中推导出
T
。很容易:它只是
T
=
array<UniqT1, uniqv1>
。然后我们也尝试反过来:每个
T
都是一个
array<U, N>
吗?为了确定这一点,我们用
UniqT2
替换
T
并尝试在
array<U, N>
中推导出
U
和
N
。由于类型
UniqT2
是“唯一”的,无论
U
和
N
是什么,都无法使
array<U, N>
成为与
UniqT2
相同的类型。
同样地,当你在#3中用一个独特的类型替换V
时,你可以推导出#2中的U
和N
,但反过来是不行的:用一个独特的类型和值替换#2中的U
和N
,然后你无法推导出#3中的V
,因为作为一个独特的值,N
并不等于1。这意味着#3比#2更为专门化。
现在关于标准措辞:
"合成一个独特的类型/值"规则是[temp.func.order]/3。
然后[temp.func.order]/4告诉你在替换独特的类型/值之后执行推断。
[temp.deduct.partial]/10告诉你如何解释结果:如果推断只在两个方向中的一个成功,那么哪个模板的参数是可以被推断的,那个模板就是较不专业的。
在调用的上下文中,我还应该提到根据[temp.deduct.partial]/3.1,在调用中没有提供实际参数的任何参数类型都将被忽略。
规则的细节当然非常复杂,所以我的总结“每个X都是Y,但反之不成立”远不能解释整个问题。额外的规则涵盖了引用类型的参数、比较非静态成员函数与静态成员函数以及可变参数模板等情况。
1
。 - Jan Schultke1
。 - undefined