模板参数包何时推断为空?

When are template parameter packs deduced as empty?

本文关键字:参数 包何时      更新时间:2023-10-16

考虑以下示例(Coliru 链接):

template <class... T> struct S { using type = int; };
template <class... T>
void f(typename S<T...>::type) {
static_assert(sizeof...(T) == 0);
}
template <class... T>
void g(typename S<T...>::type, S<T...>*) {}
int main() {
f(42);
g(42, nullptr);
}

GCC和Clang都对f的召唤感到满意,但对g的召唤却不满意。

在对f的调用中,虽然T...出现在非推导的上下文中,但它最终被推导为空。这似乎是由于 [temp.arg.explicit]/4:

。未以其他方式推导的尾随模板参数包 ([temp.variadic]) 将被推导为模板参数的空序列。...

然而,在对g的呼吁中,T...出现在推导的上下文中,导致演绎尝试和失败,这一事实似乎导致g变得不可行。一旦尝试扣除并失败,似乎没有"后备"T...空。

  • 这种行为是有意的吗?如果是这样,为什么?
  • 如果是这样,是否打算用"未以其他方式推断"的措词具体说明这种行为?(,这意味着空回退仅在包没有出现在推导的上下文中时才会发生)
  • 如果是这样,这个措辞是否足够清楚?似乎对"未以其他方式推断"的合理替代解读是"要么没有进行演绎,要么尝试演绎但失败了"。

...尾随模板参数包 ([temp.variadic]) 不是其他 推导将推导为模板参数的空序列。 ...

可以说,未以其他方式推导的不是应该以某种方式或自动放宽其他规则的条款,或者实际上它与它为什么格式不正确无关(与我认为你暗示的相反)。

另一个规则也许可以用另一个非常简单的例子来最好地证明:

template<class T>
void f(T, T){};
int main() {
f(int{42},short{42});
}

以上编译失败。为什么?因为即使short无缝转换为int(促销),它也不是同一类型。

此外,由于nullptr只是具有某种普通类型的std::nullptr_t- 因此根本不适合参与模板参数推导。

因此,让我们暂时忘记非推导的上下文,并尝试使用推导的上下文:

template <class... T>
void g(S<T...>*, S<T...>* ) {}
int main() {
S<> s1;
g(&s1, nullptr);
}

或者如果你愿意,只需

int main() {
S<> s1;
g(&s1, 0);
}

两者都因同样的原因而失败。

现在,如果您想允许转换 - 请使用身份模板 - 这甚至适用于非推断上下文!

对于您的情况,示例可能如下所示 (c++2a):

template <class... T>
void g(typename S<T...>::type, std::type_identity_t<S<T...> >*) {}
int main() {
f(42);
g(42, nullptr);
} 

这是有效的。(注意,如果你没有 C++20,只需自己编写标识模板)

正如评论中所述,扭转问题可能会导致一个更有趣的问题?

在非推导上下文中允许空模板参数推导的原因是什么?