【问题标题】:Ambiguous template with SFINAE dummy parameter带有 SFINAE 虚拟参数的模棱两可的模板
【发布时间】:2013-07-01 20:01:05
【问题描述】:

考虑一种情况,需要在另一个模板的虚拟参数中使用另一个模板 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?

【问题讨论】:

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


    【解决方案1】:

    当出现歧义时,使用模板的部分排序来解决它。但是,这种部分排序是在模板上建立的,因为它们是 before 任何替换发生,而不是 after(部分或完全)替换已经执行 - 这就是你期望通过将T 替换为typename g&lt;T&gt;::type 中的int,从而产生typename g&lt;int&gt;::type,因此(由于g 的定义)void

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

    使用两组类型来确定偏序。对于涉及的每个模板,都有 原始函数类型和转换后的函数类型。 [注:转换类型的创建 在 14.5.6.2 中描述。 --end note ] 推演过程使用转换后的类型作为参数 模板和另一个模板的原始类型作为参数模板。这个过程做了两次 对于偏序比较中涉及的每种类型:一次使用转换后的 template-1 作为 参数模板和模板 2 作为参数模板,并再次使用转换后的模板 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 既不比案例 A 更专业也不比案例 A 更专业,当两者都可行时,您会得到歧义。

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

    在大多数情况下,所有模板参数都必须具有值才能成功进行推演,但对于部分 排序目的,模板参数可能没有值,前提是它没有在类型中使用 用于部分排序。 [ 注意:考虑在非推导上下文中使用的模板参数 用过的。 ——尾注 ]

    【讨论】:

    • 我知道替换在其中不起作用,但我主要困惑为什么在偏序算法中仍然考虑具有依赖类型的参数(我希望它被忽略) .
    • @fzlogic: 恐怕“为什么”是一个非建设性的问题 :) 但标准明确规定了必须考虑它们的事实(请参阅我在答案结束)
    • 标准中的引用是我在寻找“为什么”问题时所寻找的。幸运的是,解决方法很简单:只需将 Case A 中的 void 更改为任意类型。
    • +1 修正了 显然情况 A 更专业,因为在这种情况下 B 更专业一点更明显: )
    • 通过stackoverflow.com/questions/58960177阅读本文。但是进一步遵循部分排序规则,为什么在给定“合成唯一类型”DummyT 的情况下,template &lt;class T&gt; void test(f&lt;T, void&gt;); test(f&lt;DummyT*, g&lt;DummyT&gt;::type&gt;{}); 的模板参数推导会失败? “合成的唯一类型”是否表现得好像它的合成还可以涉及声明尚不可见但在未来实例化时可能可见的其他模板特化?
    猜你喜欢
    • 2015-12-14
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    • 1970-01-01
    • 2012-12-09
    • 1970-01-01
    • 2021-07-26
    • 1970-01-01
    相关资源
    最近更新 更多