通过依赖类型使用非类型模板参数的单类型模板参数类模板的部分特化

dfr*_*fri 13 c++ language-lawyer

以下所有标准参考均指N4659:2017 年 3 月后 Kona 工作草案/C++17 DIS


考虑以下片段:

#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() {}
Run Code Online (Sandbox Code Playgroud)

static_assert(A)是成功的GCC,但失败了锵:

error: static_assert failed due to 
       requirement 'std::is_same_v<char, bool>' ""
Run Code Online (Sandbox Code Playgroud)

本质上,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;
}
Run Code Online (Sandbox Code Playgroud)

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

海湾合作委员会:

error: variable '`A<num<1> > a`' has initializer but incomplete type
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

bog*_*dan 3

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

[temp.deduct.type]/18适用于此处:

如果P有一个包含 的表单<i>,并且 的类型与由封闭的simple-template-idi命名的模板的相应模板参数的类型不同,则推导失败。[...]

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

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

如果由于template-parameter-listtemplate-id的结构而无法推导出部分特化的模板实参,则该程序是格式错误的。


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


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