c++ - 具有 SFINAE 虚拟参数的模棱两可的模板

标签 c++ template-specialization template-meta-programming sfinae

考虑这样一种情况,其中需要使用内部的另一个模板 g(例如,可能是一些 enable_if 表达式)验证类型 T另一个模板的虚拟参数,如下所示:

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; }

在这里,为了简单起见,g 并没有真正做任何事情。 Case B 中的第二个参数处于非演绎上下文中,因此直觉上人们会认为 Case BCase 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?

最佳答案

当出现歧义时,使用模板的偏序来解决它。但是,此部分排序是在模板上建立的,因为它们是任何替换发生之前,而不是(部分或完全)替换已执行之后 - 这就是你的意思期待通过替换 int对于 Ttypename g<T>::type , 产生 typename g<int>::type因此(由于 g 的定义)void .

第 14.8.2.4/2 段指定了如何建立偏序(有关以下段落的更详细讨论,请参阅 this answer on SO):

Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type and the transformed function type. [ Note: The creation of the transformed type is described in 14.5.6.2. —end note ] The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template. This process is done twice for each type involved in the partial ordering comparison: once using the transformed template-1 as the argument template and template-2 as the parameter template and again using the transformed template-2 as the argument template and template-1 as the parameter template.

在任何替换之前,不知道什么值 T将假定,您无法判断(编译器也无法判断)case B 是否比 case A 更专业或更不专业。因此,这两个专业都不比另一个更专业。

换句话说,问题不在于这个偏特化是否:

template<class T> struct f<T, void>; // Case A

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

template<class T> struct f<T*, void>; // Case B

如果那是您所拥有的,答案显然是案例 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 既不比案例 A 更专业也不比案例 A 更专业,并且当两者都可行时,你会产生歧义。

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

In most cases, all template parameters must have values in order for deduction to succeed, but for partial ordering purposes a template parameter may remain without a value provided it is not used in the types being used for partial ordering. [ Note: A template parameter used in a non-deduced context is considered used. —end note ]

关于c++ - 具有 SFINAE 虚拟参数的模棱两可的模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17412686/

相关文章:

c++ - 如何专门化模板子类?

c++ - 推导第一个模板参数与默认的其他模板参数

c++ - 还有其他不使用数组来反转字符串的方法吗?

C++ 如何简化这个模板,使其不专门化?

c++ - 专门的模板是否需要专门的声明?

c++ - 如何对 std::index_sequence 进行前缀和?

c++ - ADL 在特定情况下不起作用

c++ - 为什么typedef相同的标识符

c++ - 带有指向自身指针的 Arduino 结构泄漏内存

c++ - 避免映射键的重复存储