【问题标题】:Why does Clang prefer the primary template over the specialization from C++17?为什么 Clang 更喜欢主模板而不是 C++17 的专业化?
【发布时间】:2021-06-18 19:03:03
【问题描述】:

下面的program是从这个question中的代码缩减而来的:

template <typename T, void (*)(T), typename = void>  
struct S;

template <typename T, void (*f)(T)>
struct S<T, f, void> {};

S<int const, nullptr> s;

在所有版本的 GCC 中,在所有语言修订版中,当 s 被实例化时,会选择 S 的特化。

在所有版本的 Clang 中,但仅从 C++17 开始,在实例化 s 时会选择主模板。

我认为值得注意的几点是,如果实例化结束&lt;int, nullptr&gt;,则永远不会选择主参数,即第一个参数不再是int const。此外,如果第二个参数中的函数指针的签名不包含T 作为参数,即如果第二个参数是T (*)()void (*)(),则永远不会选择主参数。

如果这段代码不是 IFNDR,哪个编译器是正确的? C++17 语言版本是否有一些重大变化?

【问题讨论】:

  • 主模板和专业化之间的区别在于第三个参数......无论如何都是void。 so clang bug 或 ILNDR IMO。
  • 详细分析可能会有所帮助:尝试了insightc++(基于clang)并尝试了更小的场景(删除了默认参数)。 C++17 编译失败,但之前没有:cppinsights.io/s/da6745e4 我没有看到任何格式错误的代码,也没有理解错误消息,即没有真正的专业化。
  • PS:至少我减少的问题与stackoverflow.com/questions/51779059/…有点相关
  • [temp.class.spec.match]/3 (C++14) 在 C++17 ([temp.class.spec.match]) 中作为 P0127R2 的一部分被删除,并指出它是“多余的”;也许 Clang 对此采取了行动。无论哪种方式,如果我推测,这可以说与 P0127R2 及其对令人困惑的部分排序规则的更改有关。

标签: c++ templates language-lawyer template-specialization


【解决方案1】:

这是因为 C++17 允许从非类型参数的类型推导出模板类型参数。 [temp.deduct.type]/13:

当参数的值对应一个非类型模板时 用依赖类型声明的参数P 推导出自 一个表达式,推导出P类型的模板参数 从值的类型。

所以当我们尝试将S&lt;int const, nullptr&gt; 与偏特化进行匹配时,我们从两个来源推导出偏特化的模板参数T

  • 从第一个模板参数int const,我们推导出T = int const
  • 从第二个模板参数(其类型为 void (*)(int),因为函数参数的顶级 cv 限定被调整掉了),我们推导出 T = int

由于我们推导出的结果相互矛盾,因此推导失败,部分特化不匹配。

早在 2019 年就在核心反射器上提出了类似的示例。人们一致认为这是标准中的一个缺陷,并且从非类型模板参数的类型中扣除应该只发生在非类型模板参数的情况下。否则可扣除。

【讨论】:

  • Addendum/extra: [diff.cpp14.temp] 被添加为 P0127R2 的一部分,此更新 [temp.deduct.type] 实际上是一个重大更改(尽管其中的示例可以说包括更多扣除结果冲突的不透明情况,与现在找到另一个函数的重载解决方案相比)。
  • 很高兴您指出参数的顶级 cv 限定符不是函数签名的一部分(例如,用 insightc++ 很高兴看到)!但是为什么这不是编译错误呢?缺陷的部分/后果?此规则与基本模板相同,不应影响部分特化本身(另一类匹配查找)。
  • 所以即使假设具有依赖类型“依赖”的非类型参数会导致特殊的有问题的推导方案,人们可​​能会问为什么没有选择专业化,因为第一个模板参数类型仍然明确提供,对于第二个,顶级限定符的规则也适用于基本模板,并且应该更喜欢专业化,因为在这个前提下它确实比基本模板更专业。所以非正式地说:IMO,专业化实际上并没有在这里引入进一步的歧义/冲突。或者我错过了什么?
  • @Secundi 不进行任何推导来决定是否使用主模板。如果没有匹配的部分(和显式)特化,则使用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 2022-10-15
  • 2012-06-11
  • 2015-08-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多