在对"(部分)专门化依赖类型的非类型模板参数"这篇文章的回答中,它说:
与专门化对应的模板形参的类型的形参不应依赖于专业化。(例子:
template <class T, T t> struct C {}; template <class T> struct C<T, 1>; // error template< int X, int (*array_ptr)[X] > class A {}; int array[5]; template< int X > class A<X,&array> { }; // error
-end example]
我的问题是为什么这里有这个限制?至少在一个用例中,我发现这个限制妨碍了编写干净的代码。例如
template <typename T, T*>
struct test;
template <typename T>
struct test<T, nullptr> // or struct test<T, (T*)nullptr>
{
};
template <typename R, typename...ARGs, R(*fn)(ARGs...)>
struct test<R(ARGs...), fn>
{
};
虽然我不确定是否还有其他情况下,根据类型声明常量是一个问题,而不是没有任何意义。
有人知道为什么会这样吗?
标准不允许特定功能的最常见原因是:
- 该特性由语言中的另一种机制覆盖,使其成为多余的。
- 它与现有的语言逻辑和实现相矛盾,使其实现具有潜在的代码破坏。
- 遗留:这个特性一开始就被忽略了,现在我们构建了很多没有它的东西,它几乎被遗忘了(参见部分函数模板专门化)。
实现的难度很少是一个因素,尽管编译器实现可能需要一些时间才能赶上"硬"东西的发展。
你总是可以用另一种类型包装你的非类型模板参数:
template < typename T1, typename T2 >
struct Demo {}; // primary template
template < typename T >
struct Demo<T, integral_constant<T, 0>> {}; // specialization
我怀疑这个hack是否属于情形1。情况3总是有可能的,所以让我们看看情况2。要做到这一点,我们必须知道标准对类模板部分专门化施加了哪些相关规则。
14.5.5 类模板部分特化
如果非类型实参是非类型形参的名称,则是非特化的。所有其他非类型参数都是专门化的。 (C1)
在类模板部分特化的参数列表中,应用以下限制:
- 部分专门化的非类型实参表达式不应该包含部分专门化的模板形参,除非实参表达式是一个简单标识符。 (C2)
- 与专门化非类型实参对应的模板形参的类型不应依赖于专门化的形参。 (C3)
我标记了我发现相关的前三个C条款(第三个是有问题的条款)。在的例子中,根据C1,我们有一个特殊的非类型参数,所以C2应该成立,但是这个
template <class T, T t> struct C {};
template <class T> struct C<T, 1>;
实际上是
template <class T, T t> struct C {};
template <class T> struct C<T, T(1)>; // notice the value initialization
表示部分专门化的非类型参数T t
在表达式中包含部分专门化class T
的模板形参,而不是标识符;此外,这种专门化必然会在值初始化中涉及class T
,这将总是违反规则。然后C3就把它消掉了这样我们就不用每次都做这个推导了。
类模板部分特化的匹配[temp.class.spec.match]
描述了(大致)模板专门化中涉及的"模式匹配"过程。规则1
是整个过程的工作流,随后的规则是定义正确性的规则
如果部分专门化的模板实参可以从实际模板实参列表中推导出来,则部分专门化匹配给定的实际模板实参列表
也可以从主模板的非类型形参的实际模板实参的值推导出非类型模板实参。
在引用类模板专门化的类型名中(例如,a),实参列表应与主模板的模板形参列表匹配。专门化的模板参数是从主模板的参数推导出来的。
允许与专门化非类型实参相对应的模板形参的类型依赖于专门化的形参,而不违反这些规则。所以在我看来没有什么特别的理由为什么我们不能在语言的未来版本中拥有这个特性:的遗留问题要归咎于。遗憾的是,我没有找到任何语言提案来主动引入这个特性。