从属非类型参数包:标准怎么说?

Bro*_*thy 8 c++ templates sfinae language-lawyer variadic-templates

我认为以下代码格式正确:

template< typename T >
using IsSigned = std::enable_if_t< std::is_signed_v< T > >;

template< typename T, IsSigned< T >... >
T myAbs( T val );
Run Code Online (Sandbox Code Playgroud)

其他人则说它格式错误,因为C ++ 17标准的第17.7(8.3)节:

知道哪些名称是类型名称,就可以检查每个模板的语法。该程序格式错误,无需诊断,如果:(...)可变参数模板的每个有效专业化都需要一个空模板参数包,或者(...)

我认为这IsSigned< T >...是一个依赖的模板参数,因此无法在模板定义时对照§17.7(8.3)进行检查。IsSigned< T >例如可能是voidTs的一个子集,int另一个子集或替换失败。对于void子集,确实是这样,空模板参数包将是唯一有效的专业化,但是int子集可以具有许多有效的专业化。这取决于实际T参数。

这意味着编译器必须在模板实例化之后检查它,因为T之前是未知的。到那时,完整的参数列表是已知的,可变参数为零。该标准规定以下内容(第17.6.3(7)节):

当N为零时,扩展的实例化将生成一个空列表。这样的实例化不会改变封闭结构的句法解释

这就是为什么我认为它的格式正确。

  • 你怎么看?
  • 我如何确定这种歧义?这很难决定,因为代码可以编译,但是它什么都没有:§17.7(8.3)是NDR,编译器不必引发任何编译错误。

L. *_* F. 8

代码格式错误,不需要诊断。

如果std::is_signed_v<T>,则std::enable_if_t<std::is_signed_v<T>>表示类型void。否则,std::enable_if_t<std::is_signed_v<T>>不表示有效类型。因此,的每个有效专业化都myAbs需要一个空的模板参数包。

根据[meta.rqmts] / 4,如果程序std::enable_if是专用的,则该程序具有未定义的行为。因此,前述行为不能改变。

我认为这IsSigned< T >...是一个依赖的模板参数,因此无法在模板定义时对照§17.7(8.3)进行检查。IsSigned< T >例如可能是的void一个子集Tint另一个子集或替换失败。对于void子集,确实是这样,空模板参数包将是唯一有效的专业化,但是int子集可以具有许多有效的专业化。这取决于实际T 参数。

编译器无法以无法解决任意方程式的相同方式对其进行检查。NDR(无需诊断)正是针对此类情况而制作的-程序格式错误,如果编译器实际上能够检测到该错误,则将需要进行诊断。NDR允许编译器不检查它。

当N为零时,扩展的实例化将生成一个空列表。这样的实例化不会改变封闭结构的句法解释。

我们所讨论的规则是语义规则,而不是句法规则,因为句法规则在[gram]中。


那么NDR规则的依据是什么?通常,它们解决的是实施策略之间无法重现的问题。例如,它们可能会导致代码在某些实现策略中行为不当,但不会在其他实现策略中引起任何问题(也不容易)。


另外,请注意,该标准在程序方面使用诸如“格式错误”之类的术语。因此,谈论隔离代码段的格式正确性并不总是合理的。在这种情况下,std::enable_if不需要专门,但是情况可能变得更加复杂。