当出现歧义时,可以使用模板的部分排序来解决它。然而,这种部分排序是在模板上建立的,因为它们是before任何替换都会发生,而不是after(部分或全部)替换已执行 - 这是您所期望的替换int
for T
in typename g<T>::type
,这产生typename g<int>::type
因此(因为定义g
) void
.
第 14.8.2.4/2 段指定了如何建立部分排序(参见这个答案关于SO https://stackoverflow.com/a/17008568/1932150有关以下段落的更详细讨论):
使用两组类型来确定部分排序。对于每个涉及的模板都有
原始函数类型和转换后的函数类型。 [ 注:转换类型的创建
14.5.6.2 中描述。 ——尾注] 推导过程使用转换后的类型作为参数
template 和另一个模板的原始类型作为参数模板。这个过程要进行两次
对于偏序比较中涉及的每种类型:一次使用转换后的 template-1 作为
参数模板和 template-2 作为参数模板,并再次使用转换后的 template-2
作为参数模板,template-1 作为参数模板。
在任何替换之前,不知道什么值T
会假设,你无法判断(编译器也无法判断)是否case B或多或少比专业化case A。因此,这两个专业都不比另一个专业更专业。
换句话说,问题不在于这种部分专业化是否:
template<class T> struct f<T, void>; // Case A
比这个更专业(通过部分替换获得):
template<class T> struct f<T*, void>; // Case B
如果那是你所拥有的,答案显然是case B更加专业化。相反,问题是是否为了任何可能的T
,这个专业:
template<class T> struct f<T, void>; // Case A
比这个更专业:
template<class T> struct f<T*, typename g<T>::type>; // Case B
因为这不能为任何T
,情况 B 既不比情况更专业,也不比情况更不专业case A,当两者都可行时,你就会产生歧义。
如果您想知道部分排序是否考虑非推导上下文中的参数,第 14.8.2.4/11 段的注释中提到了这一点:
大多数情况下,所有模板参数都必须有值才能推导成功,但对于部分模板参数
排序目的模板参数可以保留没有值,只要它没有在类型中使用
用于部分排序。 [注意:考虑在非推导上下文中使用的模板参数
用过的。 ——尾注 ]