【发布时间】:2019-09-04 07:22:38
【问题描述】:
我认为下面的代码格式正确:
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):
知道哪些名称是类型名称可以检查每个模板的语法。如果:(...) 可变参数模板的每个有效特化都需要一个空模板参数包,或 (...)
,则程序格式错误,不需要诊断
在我看来,IsSigned< T >... 是一个依赖模板参数,因此在模板定义时无法对照 §17.7 (8.3) 检查它。 IsSigned< T > 可以是例如 void 用于 Ts 的一个子集,int 用于另一个子集或替换失败。对于void 子集,空模板参数包确实是唯一有效的特化,但int 子集可能有许多有效的特化。这取决于实际的 T 参数。
这意味着编译器必须在模板实例化之后检查它,因为之前不知道 T。在这一点上,完整的参数列表是已知的,有零个可变参数。该标准规定如下(§17.6.3 (7)):
当 N 为零时,展开的实例化产生一个空列表。这样的实例化不会改变封闭结构的句法解释
这就是为什么我认为它的格式很好。
- 你怎么看?
- 如何确定这种歧义?很难决定,因为代码可以编译,但没有任何意义:§17.7 (8.3) 是 NDR,编译器不必引发任何编译错误。
【问题讨论】:
-
在我看来,“编译器必须在之后检查它”之类的参数并不重要。标准很明确。 “可变参数模板的每个有效特化都需要一个空模板参数包”。如果您的代码确实如此,那么它的格式不正确。编译器如何在理论上检查这一点并不重要。
-
我认为最好的做法是了解为什么这条规则是标准的一部分的原因,并检查它在这种情况下是否会导致问题。这可能最接近标准的意图。
-
@geza 抱歉,
T是int在给定代码中当然不是正确的情况。我意识到,鉴于此,我或多或少会重述原始问题(“具有从属模板参数的模板的'每个有效专业化'的集合是否受从属模板参数的约束或那些无关紧要?” ),评论已删除。 -
@MaxLanghof:是的,如果有一个专门化,使
IsSigned非无效,那么代码是格式良好的。但是,如果没有这样的专业化,那么代码就是格式错误的。至少,这是我的理解,只是严格解释所写的内容。但我不确定,这是标准作者的意图。 -
@geza 我认为这种解释可能会导致格式正确的代码无法确定。不确定这是否是一个问题......我倾向于相反的解释(有效的特化集不受依赖模板参数的约束),因此即使不存在
IsSigned的非空特化,它是格式良好的(因为这样的IsSigned特化可能存在)。但我仍然认为我们应该首先根据这条规则背后的意图来回答。
标签: c++ templates language-lawyer variadic-templates sfinae