c++ - 为什么标准不允许在模板参数列表中初始化常量依赖类型?

标签 c++ templates c++11 c++14 template-meta-programming

在对这篇帖子“(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. 遗留:该功能一开始就被遗漏了,现在我们已经构建了很多没有它的东西,几乎被遗忘了(请参阅部分函数模板特化)。

实现困难很少是一个因素,尽管编译器实现可能需要一些时间才能 catch “硬”东西的发展。

您总是可以将您的非类型模板参数包装在另一种类型中:

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

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

我怀疑 this hack属于案例 1。案例 3 总是有可能的,所以让我们检查案例 2。为此,我们必须知道标准对类模板部分特化施加的相关规则。

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(一旦我们删除了初始限制,所有其他相关限制都会崩溃)。我们必须深入研究匹配类模板部分特化规则;部分排序在这里被认为超出了范围,因为如果我们可以产生有效的候选者,则由程序员来组合一个格式良好的程序(即不要创建类模板的模棱两可的使用)。

部分

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

描述了模板特化中涉及的(给予或接受)“模式匹配”过程。规则 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.

不违反这些规则,允许对应于专门化的非类型实参的模板形参的类型依赖于专门化的形参。所以恕我直言,没有具体原因为什么我们不能在未来的语言版本中拥有此功能:应该归咎于遗留问题。遗憾的是,我没有找到任何主动引入此功能的语言建议。

关于c++ - 为什么标准不允许在模板参数列表中初始化常量依赖类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32104890/

相关文章:

templates - tmpl.执行并分文件golang

c++ - move 构造函数

c++ - c++11中如何检查类成员指针是类还是结构类型?

c++ - 在 Delphi 中明确表达所有权

c++ - 是否可以代理任何功能

c++ - map<string, int> 从 const 函数调用时抛出 std::out_of_range

c++ - STL 函数/Boost 函数类型定义错误

c++ - 什么是用于序号排序的 QT 或开源 C++ 模板

c++ - 如何使用 std::imbue 设置 std::wcout 的语言环境?

c++ - 如何使用模板参数编写外联构造函数?