我认为由于措辞上的缺陷,标准不支持部分特化中的SFINAE。让我们从[temp.class.spec.match]开始:
如果给定的实参模板列表可以推导出部分特化的模板参数,则该部分特化与之匹配。
而根据[temp.deduct]中的SFINAE条款:
如果替换导致无效的类型或表达式,则类型推断失败。 无效的类型或表达式是指,如果使用替换后的参数编写,则会产生需要诊断的语法错误。[注意:如果不需要诊断,则程序仍然无效。 访问检查是替换过程的一部分。-结束说明]只有在函数类型及其模板参数类型的直接上下文中的无效类型和表达式才会导致推断失败。
CWG的略微修改的示例如下:
template <class T, class U> struct X {
typedef char member;
};
template<class T> struct X<T,
typename enable_if<(sizeof(T)>sizeof(
float)), float>::type>
{
typedef long long member;
};
int main() {
cout << sizeof(X<char, float>::member);
}
在X
上进行名称查找,找到主要内容,其中T == char,U == float
。我们查看一个部分特化,并查看它是否“匹配”-这意味着模板参数“可以推导出来”,也就是说:
+
| | arg1 arg2 |
+
| deduce T in | T | enable_if_t<(sizeof(T) > sizeof(float), float> |
| from | char | float |
+
正常的模板推导规则适用。第二个“参数”是一个不可推导的上下文,所以我们将
T
推导为
char
。
sizeof(char) > sizeof(float)
是假的,并且
enable_if_t<false, float>
是一种无效类型,因此类型推导应该失败...但是,只有在函数类型及其模板参数类型的
直接上下文中才会发生推导失败。
我们没有处理函数类型或函数模板参数类型,而是处理
类模板参数类型。类不是函数,因此如果我们按照字面意思理解,SFINAE排除应用于类时应该不适用,这样修改后的CWG示例应该导致硬错误。
然而,规则的精神似乎更接近于:
仅在推导过程的直接上下文中的无效类型和表达式才能导致推导失败。
我不知道特别排除类部分特化推导的原因是什么。此外,类模板部分特化的
部分排序看起来也像函数。从[temp.class.order]中可以看到:
对于两个类模板部分特化,如果给出以下的重写为两个函数模板,则第一个比第二个更特化,[...]
因此,标准在
下一节就展示了类模板部分特化和函数模板之间的双重性。这仅适用于部分特化排序,而不是在部分特化参数推导期间的替换失败,我认为这是一个缺陷。
†示例本身是X<double, float>
。但是,实际上这并不演示或需要SFINAE,因为任何地方都不会有替换失败。