为什么标准不允许在模板参数列表中初始化常量相关类型?

Why is initialization of a constant dependent type in a template parameter list disallowed by the standard?

在对这个 post“(Partially) specializing a non-type template parameter of dependent type”的回答中,它指出:

The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization. [ Example:

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>
{
};

尽管我不确定是否还有其他情况表明基于类型声明常量是一个毫无意义的问题。

有人知道为什么会这样吗?

(恕我直言)标准不允许特定功能的最常见原因是:

  1. 语言中的另一种机制涵盖了该功能,因此它是多余的。
  2. 它与现有的语言逻辑和实现相矛盾,使其实现可能会破坏代码。
  3. 旧版:该功能一开始就被遗漏了,现在我们在没有它的情况下构建了很多它几乎被遗忘了(请参阅部分功能模板专业化)。

实现的难度很少是一个因素,尽管编译器实现可能需要一些时间才能赶上 "hard" 东西的发展。

您始终可以将非类型模板参数包装在另一种类型中:

template < typename T1, typename T2 > 
struct Demo {}; // primary template

template < typename T > 
struct Demo<T, integral_constant<T, 0>> {}; // specialization

我怀疑 this hack 属于第一种情况。第 3 种情况总是有可能的,所以让我们检查第 2 种情况。为此,我们必须知道标准对 [=67= 施加的相关规则是什么] 模板部分专业化。

14.5.5 Class template partial specializations

  1. A non-type argument is non-specialized if it is the name of a non-type parameter. All other non-type arguments are specialized. (C1)

  2. Within the argument list of a class template partial specialization, the following restrictions apply:

    • A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier. (C2)
    • The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization. (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 出现并为我们清除它,这样我们就不必每次都进行扣除。

到目前为止,我们已经确定规则与自身同步,但这并不能证明情况 2(一旦我们删除了初始限制,所有其他相关限制就会崩溃)。我们必须深入研究 匹配 class 模板部分特化 规则;部分排序被认为超出了这里的范围,因为如果我们能够产生有效的候选者,则由程序员来组合一个格式良好的程序(即不要创建 class 模板的模糊使用)。

Matching of class template partial specializations [temp.class.spec.match]

描述了模板专业化中涉及的(给予或接受)"pattern matching" 过程。规则 1 是程序的整体工作流程,后续规则是定义正确性的规则

  1. A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list

  2. A non-type template argument can also be deduced from the value of an actual template argument of a non-type parameter of the primary template.

  3. In a type name that refers to a class template specialization, (e.g., A) the argument list shall match the template parameter list of the primary template. The template arguments of a specialization are deduced from the arguments of the primary template.

不违反这些规则允许对应于专门化非类型参数的模板参数的类型依赖于专门化的参数。所以恕我直言 没有具体原因 为什么我们不能在未来的语言修订版中拥有此功能:遗留问题是罪魁祸首。可悲的是,我没有设法找到任何主动引入此功能的语言提案。