带有SFINAE伪参数的模糊模板

Ruf*_*ind 7 c++ sfinae template-specialization template-meta-programming

考虑一种情况,需要在T另一个模板的虚拟参数内验证具有另一个模板的类型g(enable_if例如,可能是某个表达式),如下所示:

template<class>        struct g { typedef void type; };
template<class, class> struct f {};
template<class T>      struct f<T, void> {};                  // Case A
template<class T>      struct f<T*, typename g<T>::type> {};  // Case B

int main() { f<int*, void> test; }
Run Code Online (Sandbox Code Playgroud)

在这里,为了简单起见,g并没有真正做任何事情.案例B中的第二个参数位于非弱化上下文中,因此直观地认为案例B案例A更专业.可悲的是,gcc和clang都会抱怨模板在上面的实例化中是模棱两可的.

如果要删除虚拟参数,那么它编译就好了.如何添加一个非弱化参数会以某种方式破坏T*比这更专业的合理期望T

这是使用替换算法的快速检查:

   f<Q , void      >
-> f<T*, g<Q>::type> // [failed]

   f<Q*, g<Q>::type>
-> f<T , void      > // [to fail or not to fail?]
// One would assume that 2nd parameter is ignored, but guess not?
Run Code Online (Sandbox Code Playgroud)

And*_*owl 5

当出现歧义时,使用模板的部分排序来解决它.然而,这个偏序是在模板建立的,因为他们之前的任何替代发生,而不是(部分或全部)替代已执行-这是你被更换期待什么intTtypename g<T>::type,这将产生typename g<int>::type,因此(因为定义g)void.

第14.8.2.4/2段规定了如何建立部分排序(关于以下段落的更详细讨论,请参见SO上的答案):

两组类型用于确定部分排序.对于涉及的每个模板,都有原始函数类型和转换后的函数类型.[注意:转换类型的创建在14.5.6.2中描述.-end note]演绎过程使用变换后的类型作为参数模板,将另一个模板的原始类型作为参数模板.对于部分排序比较中涉及的每种类型,此过程完成两次:一次使用转换的模板-1作为参数模板,使用template-2作为参数模板,再次使用转换的模板-2作为参数模板和模板-1作为参数模板.

在任何替换之前,在不知道T将假设什么值的情况下,你无法告诉(并且编译器都不能告诉)案例B是否比案例A更专业或更不专业.因此,这两个专业都不比另一个更专业.

换句话说,问题不在于这个部分专业化:

template<class T> struct f<T, void>; // Case A
Run Code Online (Sandbox Code Playgroud)

比这个更专业(通过部分替换获得):

template<class T> struct f<T*, void>; // Case B
Run Code Online (Sandbox Code Playgroud)

如果这就是你所拥有的,那么答案显然是案例B更专业.相反,问题是,对于任何可能的T,这种专业化:

template<class T> struct f<T, void>; // Case A
Run Code Online (Sandbox Code Playgroud)

比这更专业:

template<class T> struct f<T*, typename g<T>::type>; // Case B
Run Code Online (Sandbox Code Playgroud)

由于无法确定任何T案例,因此案例B既不比案例A更专业也更不专业,并且当两者都可行时,您就会产生歧义.

如果您想知道部分排序是否考虑了非推导上下文中的参数,则在第14.8.2.4/11段的注释中提到:

在大多数情况下,所有模板参数必须具有值以便推断成功,但是对于部分排序目的,模板参数可以保持不带值,前提是它不用于用于部分排序的类型.[ 注意:使用非推断上下文中使用的模板参数.- 尾注 ]