通过依赖类型使用非类型模板参数的单类型模板参数类模板的部分专用化
Partial specialization of single type template parameter class template using non-type template parameter through a dependent type
以下所有标准参考均指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 适用:
>有趣的是,我找不到诊断此问题的编译器,甚至找不到严格模式下的EDG。我们可以推测,大多数编译器编写者认为在这里进行诊断的好处不值得实现检查。这可能意味着我们可能会看到上一段中的需求将来从格式错误更改为格式错误,无需诊断。然而,这纯粹是猜测。无论如何,我认为它不会变成格式良好的;我想不出永远不匹配的部分专业化的有效用途。如果部分专用化的模板参数不能 由于其模板参数列表的结构而推断出来,并且模板 ID,程序格式不正确。
[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 当然是无关紧要的:不涉及专门的非类型参数。
- 在 c++ 中的模板实例化中使用带有构造函数的类作为类型参数
- 如何解决一元"*"(有"字符")错误的无效类型参数?
- "std::shared_ptr":不是参数"_Ty"的有效模板类型参数
- 具有可变参数非类型参数的模板专用化
- 函数类型参数的模板参数推导
- PowerShell 使用结构类型参数调用 C++ DLL 的导出函数
- 对于非常量指针类型的参数,未调用具有常量指针模板类型参数的功能
- 为模板传递非类型参数 agument
- 为什么带有类型参数的运算符 () 可以应用于 result_of 上下文中的类型?
- 使用其他模板类型参数作为要在函数签名中使用的类型别名声明
- 如何避免具有相同类型参数的函数中的错误
- 将内置类型变量传递给只有一个类类型参数的"+"运算符函数时自动类型转换的构造函数
- c++非类型参数包扩展
- 如何实现对参数顺序不可知的std::same_as的广义形式(即对于两个以上的类型参数)
- 在不同的模板参数包之间分发非类型参数包
- 如何在使用容器和字符串时强制使用显式分配器类型参数
- 错误:一元"*"的类型参数无效(具有"int"):使用 mergesort 计算
- EXPECT_CALL具有 unique_ptr 引用类型参数的模拟函数
- 作为模板类型参数,为什么 type[N] 与其专用版本不匹配----模板<类 T>类 S<T[]>
- C++ 模板:重载时找不到基类类型参数方法