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

Partial specialization of single type template parameter class template using non-type template parameter through a dependent type

本文关键字:类型 参数 专用 依赖 单类型      更新时间:2023-10-16

以下所有标准参考均指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() {}

(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++11,C++14,C++17,C++2a(的各种C++语言级别。

问题

  • 上面的第一个代码段实际上是格式不正确的(不需要诊断?(,还是GCC或Clang错了?

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

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

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

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

如果P具有包含<i>的表单,并且i的类型不同 从模板的相应模板参数的类型 由封闭的简单模板 ID命名,扣除失败。[...]

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

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

如果部分专用化的模板参数不能 由于其模板参数列表的结构而推断出来,并且模板 ID,程序格式不正确。

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

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

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

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

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