【问题标题】:Partial specialization of single type template parameter class template using non-type template parameter through a dependent type通过依赖类型使用非类型模板参数的单一类型模板参数类模板的部分特化
【发布时间】:2020-06-29 11:09:40
【问题描述】:

以下所有标准参考均指N4659: March 2017 post-Kona working draft/C++17 DIS


考虑以下 sn-p:

#include <type_traits>

template <int N> struct num {};

template <typename> struct A;

// (1)
template <int N> struct A<num<N>> { using type = bool; };

// (2)
template <long N> struct A<num<N>> { using type = char; };

static_assert(!std::is_same_v<long, int>, "");

// (A)
static_assert(std::is_same_v<A<num<1>>::type, bool>, "");

int main() {}

(A) 处的 static_assert 对于 GCC 是成功的,但对于 Clang 是失败的:

error: static_assert failed due to 
       requirement 'std::is_same_v<char, bool>' ""

本质上,GCC 选择了完美匹配的特化 (1),而 Clang 选择了特化 (2)

同样,如果我们删除断言以及专业化(1)

template <int N> struct num {};

template <typename> struct A;

// (2)
template <long N> struct A<num<N>> { using type = char; };

int main() {
  A<num<1>> a{};
  (void)a;
}

然后 GCC 无法编译程序而 Clang 接受它。

海合会:

error: variable '`A<num<1> > a`' has initializer but incomplete type

此行为适用于各种 GCC 和 Clang 版本,以及超过这些版本的各种 C++ 语言级别(C++11、C++14、C++17、C++2a)。

问题

  • 上面的第一个 sn-p 实际上是格式错误的(不需要诊断?),还是 GCC 或 Clang 错误?

我的猜测是这是格式错误的,但无法应用 [temp.class.spec] 的相关部分来拒绝它。也许[temp.class.spec]/8.1

[temp.class.spec]/8.1 对应于专门化的非类型实参的模板形参的类型不应依赖于专门化的形参。 [ 示例:[...] — 结束示例 ]

【问题讨论】:

  • 我相当肯定违反任何 [temp.class.spec]/8 项目符号都是可以诊断的。所以专业化本身会被标记。
  • @StoryTeller-UnslanderMonica 我同意,其中的示例甚至被归类为 OKError
  • wg21.link/cwg1647类似吗?
  • @LanguageLawyer 确实很相似,我们可以将a similar example 应用到 CWG Issue 1687 被 Clang 接受但被 GCC 拒绝的一个;正如问题报告中所写,“此示例的处理存在实现差异。”。这无法解释为什么 Clang 选择提升到 long 专业化,其明显的“这是有效的 C++ 代码”解释(除非上面的示例实际上是格式错误的,不需要诊断,除了标准的歧义)完美匹配int splzation。

标签: c++ language-lawyer


【解决方案1】:

据我所知,第一个 sn-p 格式不正确(需要诊断);由于部分特化 (2),编译器应该拒绝该程序。

[temp.deduct.type]/18 在这里适用:

如果P 的表单包含&lt;i&gt;,并且如果i 的类型不同 从模板对应模板参数的类型 由封闭的simple-template-id命名,推演失败。 [...]

标准中的相关示例使用函数模板,但在其他方面非常相似。

所以部分特化 (2) 的模板参数永远无法推导出来,[temp.class.spec.match]/3 适用:

如果偏特化的模板参数不能 由于其 template-parameter-list 的结构而推导出来 template-id,程序格式不正确。


有趣的是,我找不到诊断此问题的编译器,甚至在严格模式下也找不到 EDG。我们可以推测,大多数编译器编写者认为在这里进行诊断的好处不值得为实现检查付出努力。这可能意味着我们可能会看到上面段落中的要求在未来从 ill-formed 变为 ill-formed,不需要诊断。然而,这纯属猜测。无论如何,我认为它永远不会变成格式良好的;我想不出一个从不匹配的部分专业化的有效用途。


[temp.deduct.type]/18 的措辞由CWG2091 的决议澄清。

【讨论】:

  • 谢谢,在接受这个(或另一个)答案之前,我会阅读 [temp.deduct.type] .
  • @dfri 是的,我没有注意到戴维斯的回答也提到了第 18 条中的规则。我在一开始就看到了“部分排序”,然后看了一眼其余的部分,因为在这种情况下我们肯定没有达到部分排序。如果我之前注意到,像我一样懒惰,我可能会评论他的答案,指向 [temp.class.spec.match]/3,并且会省去你在两者之间进行选择的麻烦 :-) .
【解决方案2】:

对于部分专业化 ([temp.class.spec.match]/2) 的模板参数推导,该标准不够精确,无法明确确定示例的含义。特别是,所有推导最终都是根据类型([temp.deduct.type])定义的,但不涉及非类型模板参数的类型。

偏特化中偏序的推导根据发明的类模板 ([temp.class.order]/1.2) 处理这种情况,这带来了任何 不匹配 推导失败的规则strong> 在非类型模板实参的类型与其形参 ([temp.deduct.type]/18) 之间。这使得 A&lt;num&lt;…&gt;&gt; 在您的示例中的任何使用都模棱两可 if 两个部分特化匹配(避免需要确定使用为部分排序合成的“唯一值”是否涉及缩小转换([temp .func.order]/3) 作为模板参数)。但是,如果我们将相同的规则应用于匹配本身,我们会发现(就像 GCC 一样)(2)永远不会匹配。反过来,这可以说应该引发对专业化本身的诊断([temp.class.spec.match] / 3,正如博格丹的回答所提到的),尽管如果错误是,它意味着什么“结构”并不完全清楚是可诊断的,没有编译器会拒绝它。

同时,[temp.class.spec]/8.1 肯定是无关紧要的:不涉及专门的非类型参数。

【讨论】:

    猜你喜欢
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 2018-05-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-07
    相关资源
    最近更新 更多