通过依赖类型使用非类型模板参数的单一类型模板参数 class 模板的部分特化
Partial specialization of single type template parameter class template using non-type template parameter through a dependent type
以下所有标准参考均指N4659: March 2017 post-Kona working draft/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 接受它。
GCC:
error: variable '`A<num<1> > a`' has initializer but incomplete type
此行为适用于各种 GCC 和 Clang 版本,以及这些版本的各种 C++ 语言级别(C++11、C++14、C++17、C++2a)。
问题
- 上面的第一个片段实际上格式不正确(不需要诊断吗?),或者 GCC 或 Clang 错误?
我的猜测是格式错误,但无法应用 [temp.class.spec] to reject it. Perhaps [temp.class.spec]/8.1 的相关部分?
[temp.class.spec]/8.1 The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization. [ Example: [...] — end example ]
对于偏特化 ([temp.class.spec.match]/2) 的模板参数推导,该标准不够精确,无法明确确定示例的含义。特别是,所有推导最终都是根据 types ([temp.deduct.type]) 定义的,但是没有涉及非类型模板参数的类型。
偏特化之间偏序的推导根据发明的 class 模板 ([temp.class.order]/1.2) 处理这种情况,它带来了推导失败的规则非类型模板参数的类型与其参数 ([temp.deduct.type]/18) 不匹配。这使得在您的示例中使用 A<num<…>>
不明确 if 两个部分专业化匹配(避免需要确定缩小转换是否涉及使用合成的“唯一值”部分排序([temp.func.order]/3)作为模板参数)。但是,如果我们将相同的规则应用于匹配本身,我们会发现(GCC 也是如此)(2) 永远不会匹配。反过来,这可以说应该引发对专业化本身的诊断([temp.class.spec.match]/3,正如博格丹的回答所提到的),尽管如果错误是要包含什么“结构”并不完全明显可诊断,没有编译器拒绝它。
与此同时,[temp.class.spec]/8.1 当然是无关紧要的:没有涉及专门的非类型参数。
据我所知,第一个片段格式不正确(需要诊断 );由于部分专业化 (2).
,编译器应该拒绝该程序
[temp.deduct.type]/18 适用于此处:
If P
has a form that contains <i>
, and if the type of i
differs
from the type of the corresponding template parameter of the template
named by the enclosing simple-template-id, deduction fails. [...]
标准中的关联示例使用函数模板,但在其他方面非常相似。
所以偏特化(2)的模板实参永远无法推导,[temp.class.spec.match]/3适用:
If the template arguments of a partial specialization cannot be
deduced because of the structure of its template-parameter-list and
the template-id, the program is ill-formed.
有趣的是,我找不到诊断此问题的编译器,即使是严格模式下的 EDG。我们可以推测,大多数编译器编写者认为在这里进行诊断的好处不值得实施检查的努力。这可能意味着我们可能会看到上述段落中的要求在未来从 格式错误 更改为 格式错误,不需要诊断 。然而,这纯粹是猜测。无论如何,我看不到它会变成 well-formed;我想不出从不匹配的部分专业化的有效用途。
[temp.deduct.type]/18 的措辞已由 CWG2091 的决议阐明。
以下所有标准参考均指N4659: March 2017 post-Kona working draft/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 接受它。
GCC:
error: variable '`A<num<1> > a`' has initializer but incomplete type
此行为适用于各种 GCC 和 Clang 版本,以及这些版本的各种 C++ 语言级别(C++11、C++14、C++17、C++2a)。
问题
- 上面的第一个片段实际上格式不正确(不需要诊断吗?),或者 GCC 或 Clang 错误?
我的猜测是格式错误,但无法应用 [temp.class.spec] to reject it. Perhaps [temp.class.spec]/8.1 的相关部分?
[temp.class.spec]/8.1 The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization. [ Example: [...] — end example ]
对于偏特化 ([temp.class.spec.match]/2) 的模板参数推导,该标准不够精确,无法明确确定示例的含义。特别是,所有推导最终都是根据 types ([temp.deduct.type]) 定义的,但是没有涉及非类型模板参数的类型。
偏特化之间偏序的推导根据发明的 class 模板 ([temp.class.order]/1.2) 处理这种情况,它带来了推导失败的规则非类型模板参数的类型与其参数 ([temp.deduct.type]/18) 不匹配。这使得在您的示例中使用 A<num<…>>
不明确 if 两个部分专业化匹配(避免需要确定缩小转换是否涉及使用合成的“唯一值”部分排序([temp.func.order]/3)作为模板参数)。但是,如果我们将相同的规则应用于匹配本身,我们会发现(GCC 也是如此)(2) 永远不会匹配。反过来,这可以说应该引发对专业化本身的诊断([temp.class.spec.match]/3,正如博格丹的回答所提到的),尽管如果错误是要包含什么“结构”并不完全明显可诊断,没有编译器拒绝它。
与此同时,[temp.class.spec]/8.1 当然是无关紧要的:没有涉及专门的非类型参数。
据我所知,第一个片段格式不正确(需要诊断 );由于部分专业化 (2).
,编译器应该拒绝该程序[temp.deduct.type]/18 适用于此处:
If
P
has a form that contains<i>
, and if the type ofi
differs from the type of the corresponding template parameter of the template named by the enclosing simple-template-id, deduction fails. [...]
标准中的关联示例使用函数模板,但在其他方面非常相似。
所以偏特化(2)的模板实参永远无法推导,[temp.class.spec.match]/3适用:
If the template arguments of a partial specialization cannot be deduced because of the structure of its template-parameter-list and the template-id, the program is ill-formed.
有趣的是,我找不到诊断此问题的编译器,即使是严格模式下的 EDG。我们可以推测,大多数编译器编写者认为在这里进行诊断的好处不值得实施检查的努力。这可能意味着我们可能会看到上述段落中的要求在未来从 格式错误 更改为 格式错误,不需要诊断 。然而,这纯粹是猜测。无论如何,我看不到它会变成 well-formed;我想不出从不匹配的部分专业化的有效用途。
[temp.deduct.type]/18 的措辞已由 CWG2091 的决议阐明。