【问题标题】:Class template specialization partial ordering and function synthesis类模板特化偏序和函数综合
【发布时间】:2017-07-14 00:11:12
【问题描述】:

选择首选哪个类模板特化的规则包括将特化重写为函数模板,并通过函数模板的排序规则 [temp.class.order] 确定哪个函数模板更特化。考虑这个例子,然后:

#include <iostream>

template <class T> struct voider { using type = void; };
template <class T> using void_t = typename voider<T>::type;

template <class T, class U> struct A { };

template <class T> int foo(A<T, void_t<T>> ) { return 1; }
template <class T> int foo(A<T*, void> )     { return 2; }

int main() {
    std::cout << foo(A<int*, void>{});
}

gcc 和 clang 都在这里打印 2。这对于前面的一些示例是有意义的 - 对非推导上下文进行推导(voidvoid_t&lt;T&gt;)只是被忽略,因此推导 &lt;T, void_t&lt;T&gt;&gt;&lt;X*, void&gt; 成功,但推导 &lt;T*, void&gt;&lt;Y, void_t&lt;Y&gt;&gt; 均失败论据。很好。

现在考虑这个概括:

#include <iostream>

template <class T> struct voider { using type = void; };
template <class T> using void_t = typename voider<T>::type;

template <int I> struct int_ { static constexpr int value = I; };

template <class T, class U> struct A      : int_<0> { };
template <class T> struct A<T, void_t<T>> : int_<1> { };
template <class T> struct A<T*, void>     : int_<2> { };

int main() {
    std::cout << A<int*, void>::value << '\n';
}

clang 和 gcc 都将此专业化报告为模棱两可,介于 12 之间。但为什么?合成的函数模板没有歧义。这两种情况有什么区别?

【问题讨论】:

  • 分页@bogdan.
  • 有趣,看起来像一个错误,VC++ 也报告了歧义。根据14.5.5.2,这些类应该转换为template &lt;class T&gt; void f(A&lt;T, void_t&lt;T&gt;&gt;)template &lt;class T&gt; void f(A&lt;T*, void&gt;),第二个应该像你的第一个例子一样选择
  • 我在这里 :-)。首先想到的是,这和我写的关于in this answer 的问题是一样的,但我会稍微考虑一下。
  • @Bogdan void_t 的“无序”问题是 CWG 2173。
  • @MassimilianoJanes 在您的示例中,您将合成类型固定为具体的众所周知的类型,因此您没有对通用算法进行建模。如果您想在答案的最后一个示例中添加相关的voider 专业化,请添加template&lt;&gt; struct voider&lt;T0_&gt; { using type = U0_; };。这将更改您示例中的结果,但不会更改编译器实际用于部分排序的算法。要测试编译器的算法,您可以使用this one 之类的示例;我在代码 cmets 中包含了一些解释。

标签: c++ templates language-lawyer partial-ordering


【解决方案1】:

Clang 与 GCC 兼容(并且与依赖于这两种行为的现有代码兼容)。

考虑 [temp.deduct.type]p1

[...] 尝试查找模板实参值(类型形参的类型、非类型形参的值或模板形参的模板),在替换推导的值(称其为推导的A),与A兼容。

问题的症结在于这里的“兼容”是什么意思。

当对函数模板进行部分排序时,Clang 只是双向推导;如果推演在一个方向上成功但在另一个方向上没有成功,则假设结果将是“兼容的”,并将其用作排序结果。

但是,当对类模板部分特化进行部分排序时,Clang 将“兼容”解释为“相同”的意思。因此,如果将其中一个推导的参数替换为另一个会重现原始的部分专业化,它只会认为一个部分专业化比另一个更专业化。

更改这两者中的任何一个以匹配另一个会破坏大量实际代码。 :(

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-11
    • 1970-01-01
    相关资源
    最近更新 更多