【问题标题】:How do variadic type template parameters and non-type template parameters of the template template parameter of a nested class constrain each other?嵌套类的模板模板参数的可变类型模板参数和非类型模板参数如何相互约束?
【发布时间】:2021-03-08 22:53:54
【问题描述】:

考虑以下结构:S 是一个类模板,其中包含模板类型参数...Ts 的可变参数包。该类包含一个嵌套类模板N,它有一个模板模板参数CC 本身是使用一组非类型模板参数的可变参数包进行模板化的,这些参数的类型与 Ts... 完全相同。

template <typename ...Ts>
struct S 
{
    template <template <Ts...> typename C>
    struct N 
    {
        C<42> c;
    };
};

GCC 拒绝 c 的声明:

error: expansion pattern '<anonymous>' contains no parameter packs
        C<42> c;
            ^

我不知道这个诊断意味着什么,我猜这是一个 GCC 错误。


Clang 接受这种结构,也接受合理的实例化,例如:

template<int> struct W {};
S<int>::N<W> w;             // ok, makes sense

当然,声明C&lt;42&gt; c; 本身对用作参数C 的参数的模板施加了约束,即使C 中的Ts... 只是auto...,这也是必需的。例如Ts... 的第一个参数必须是可从 int 隐式转换的类型。此外,Ts... 中的其余参数(如果有)必须具有默认值。

template<bool(*)()> struct X1 {};
S<int>::N<X1> x1;                  // error: value of type 'int' is not implicitly convertible to 'bool (*)()'
        
template<int, char> struct X2 {};
S<int>::N<X2> x2;                  // error: too few template arguments for class template 'X2'

有趣的是,...TsTs... 之间的关系似乎确实存在限制。例如S 的所有指定参数现在必须是有效的非类型模板参数类型:

template<int> struct Y {};
S<int, void>::N<Y> y;       // error: a non-type template parameter cannot have type 'void' 

另一方面,Clang 还接受 S 的看似不兼容的参数的实例化,以及 C 的非类型模板参数:

template<int> struct Z {};
S<bool(*)(), bool>::N<Z> z;  // ok ?? But why? both number and type of 'Ts' is different 

这是demo

那么...TsTs... 在这个结构中如何相互约束的规则是什么?


我在尝试理解 question 时遇到了这个问题,它表明 MSVC 也不接受代码。

【问题讨论】:

  • 一个有趣的问题是,当命名为C 时,默认参数是否应该应用于Z。如果是,那么检查N 更难;如果不是,那么 gcc 是对的。
  • @Yakk-AdamNevraumont 我不确定我是否理解为什么默认参数很重要。如果N 中的声明是C&lt;1&gt; n;,那么b 也被接受,X 没有默认参数。
  • 即使没有预期的默认值,gcc 也会抱怨Demo
  • @cigien 如果默认参数未“通过”,那么您的模板仅对空包有效,我上次检查时这是非法的(格式错误,ndr)。 C&lt;1&gt; case 没有这个问题。
  • @cigien 此外,仅包含数字 42 的参数包也是格式错误的,ndr - 这就是所谓的毛巾规则。 (这是个玩笑,因为在互联网上传达幽默如果不明确是很困难的)

标签: c++ templates language-lawyer variadic-templates non-type


【解决方案1】:

对于您的主要问题(具有依赖于外部类中的模板参数的非类型模板参数的嵌套类),这是 GCC 中的一个错误,#86883(参见 comment #3

【讨论】:

  • 非常感谢您跟踪该错误。这实际上不是我的主要问题,正如我提到的,我认为这是一个 gcc 错误。我的主要问题是询问 约束如何工作。我看到问题标题具有误导性。我会编辑它。
  • 有趣的是,该错误报告已有 2 年历史,但仍未得到确认。
  • @cigien:是的,我看到了。似乎与其他相同的错误有关。所有这些都可能与有关在模板中处理 lambdas 的变更集有关。旁白:我想找个好地方说这个,但不确定答案是否是最好的:我猜允许S&lt;bool(*)(), bool&gt;::N&lt;Z&gt; z; 是一个错误,但我还不能证明这一点。我猜测 MSVC 和 clang 将其视为对同一模板的重新声明,然后错误地允许实际类型具有不同的模板参数。
  • @cigien Heck,Clang 允许S&lt;bool, bool, bool, bool, unsigned long long, char, short, unsigned char, long long&gt;::N&lt;Z&gt; z;,这似乎不对;每个 [temp.arg.template]
  • 是的,我认为最后一个 sn-p 违反了几条规则。 Clang 可能还没有实现这些检查。有趣的问题是,应该允许什么的规则是什么?我发现这个标准特别难以理解:(
【解决方案2】:

我觉得Clang不对。
temp.param#15

作为参数声明的模板参数包,其类型包含一个或多个未扩展的参数包,是包扩展。

对于嵌套模板类N的模板模板参数,即template &lt;Ts...&gt; typename C,其中Ts...是一个pack实例化,其规则如下:

每个 Ei 都是通过实例化模式并将每个包扩展参数替换为其第 i 个元素而生成的。这样的元素,在实例化的上下文中,解释如下:

  • 如果包是模板参数包,则元素是相应种类(类型或非类型)的模板参数,指定模板参数的类型或值;

因此,对于此示例 S&lt;bool(*)(), bool&gt;::N&lt;Z&gt; z; ,实例化 Ts... 将给出一个列表 bool(*)(), bool。因此,问题可以简化为:

template<template<bool(*)(), bool> class C>
struct Nested{};
template<int> struct Z {};
int main(){
  Nested<Z> z; // is well-formed?
}

temp.arg.template#3

当 P 至少与模板参数 A 一样特化时,模板参数匹配模板模板参数 P。

参数Z 是否与参数C 匹配?根据这条规则temp.arg.template#4

如果给定以下对两个函数模板的重写,则模板模板参数 P 至少与模板模板参数 A 一样专门化,则对应于 P 的函数模板至少与对应于 A 的函数模板一样专门化根据功能模板的偏序规则。给定一个发明的类模板 X,其模板参数列表为 A(包括默认参数):

  • 两个函数模板中的每一个都具有相同的模板参数,分别为 P 或 A。
  • 每个函数模板都有一个函数参数,其类型是 X 的特化,模板参数对应于相应函数模板中的模板参数,其中,对于函数模板的模板参数列表中的每个模板参数 PP,对应的模板参数 AA 形成。如果 PP 声明了一个参数包,那么 AA 就是包扩展 PP... ([temp.variadic]);否则,AA 是 id-expression PP。

如果重写产生无效类型,则 P 至少不如 A 专门化。

这意味着,我们有一个发明的模板类 X 具有以下形式:

template<int>
struct InventedX{};

那么,A 的重写函数模板的形式为:

template<int N>
auto iventend_function(X<N>);

P 的重写函数模板具有以下形式:

template<bool(*A)(), bool B>
auto invented_function(X<A,B>) // invalid type for X<A,B>

所以,P 至少不像 A 那样专业。因此A 不能与P 匹配。所以,S&lt;bool(*)(), bool&gt;::N&lt;Z&gt; z; 应该是格式错误的。

【讨论】:

    猜你喜欢
    • 2014-06-07
    • 2021-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-26
    相关资源
    最近更新 更多