依赖非类型参数包:标准怎么说?

Dependent non-type parameter packs: what does the standard say?

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

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

template< typename T, IsSigned< T >... >
T myAbs( T val );

也有人说是病式的,因为C++17标准的§17.7(8.3):

Knowing which names are type names allows the syntax of every template to be checked. The program is ill-formed, no diagnostic required, if: (...) every valid specialization of a variadic template requires an empty template parameter pack, or (...)

在我看来 IsSigned< T >... 是一个从属模板参数,因此在模板定义时不能根据 §17.7 (8.3) 检查。例如,IsSigned< T > 对于 Ts 的一个子集可能是 void,对于另一个子集或替换失败可能是 int。对于 void 子集,空模板参数包确实是唯一有效的特化,但是 int 子集可以有许多有效的特化。这取决于实际的 T 参数。

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

When N is zero, the instantiation of the expansion produces an empty list. Such an instantiation does not alter the syntactic interpretation of the enclosing construct

这就是我认为它格式正确的原因。

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

如果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 是特化的,则程序具有未定义的行为。因此,无法更改上述行为。

In my opinion IsSigned< T >... is a dependent template parameter, therefore it can not be checked against §17.7 (8.3) in template definition time. IsSigned< T > could be for example void for one subset of Ts, int for another subset or substitution failure. For the void subset it is true, that the empty template parameter pack would be the only valid specialization, but the int subset could have many valid specializations. It depends on the actual T argument.

编译器无法检查它,就像它无法为您求解任意方程一样。 NDR(不需要诊断)正是为这种情况而设计的——程序格式错误,如果编译器确实能够检测到,则需要进行诊断。 NDR 允许编译器不检查它。

When N is zero, the instantiation of the expansion produces an empty list. Such an instantiation does not alter the syntactic interpretation of the enclosing construct.

我们说的规则是语义规则,不是句法规则,因为句法规则是以[gram]为单位的。


那么 NDR 规则的基本原理是什么?一般来说,它们解决的问题在实施策略中是不可重现的。例如,它们可能会导致代码在某些实现策略中行为不当,但不会在其他方面造成任何问题(并且不容易)。


此外,请注意标准在程序方面使用 "ill-formed" 等术语。因此,谈论一个孤立代码片段的格式是否正确并不总是合理的。在这种情况下,std::enable_if需要不特化,否则情况可能会变得更复杂。