考虑以下示例 ( Coliru link ):
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 :
... A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments. ...
然而,在对 g
的调用中,T...
另外出现在推导的上下文中,这会导致推论尝试但失败,似乎导致g
变得不可行。一旦尝试推导失败,似乎就没有“后备”了 T...
为空。
- 这种行为是故意的吗?如果是这样,为什么?
- 如果是这样,“不以其他方式推断”的措辞是否旨在指定此行为? (即,这意味着仅当该包出现在没有推导的上下文中时才会发生空回退)
- 如果是这样,这个措辞是否足够清楚?似乎“未以其他方式推论”的另一种合理解释是“要么未进行推论,要么尝试推论但失败了”。
最佳答案
... A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments. ...
可以说,不是以其他方式推导的不是一个应该以某种方式或自动放松某些其他规则的子句,或者实际上它与格式错误的原因无关(与我想你是在暗示)。
这个其他规则也许可以通过另一个非常简单的例子得到最好的证明:
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,请自行编写身份模板)
如 comment 中所述,扭转问题或许会引出一个更有趣的问题?
在非推导上下文中允许空模板参数推导的原因是什么?
关于c++ - 模板参数包什么时候被推导为空?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57174400/