C++ 模板参数包何时推断为空?
考虑以下示例():C++ 模板参数包何时推断为空?,c++,templates,language-lawyer,variadic-templates,c++2a,c++20,C++,Templates,Language Lawyer,Variadic Templates,C++2a,C++20,考虑以下示例(): 模板结构{使用type=int;}; 模板 void f(typename S::type){ 静态断言(sizeof…(T)==0); } 模板 void g(typename S::type,S*){} int main(){ f(42); g(42,nullptr); } GCC和Clang都对调用f感到满意,但对g的调用并不满意 在对f的调用中,虽然T.出现在非推断上下文中,但最终被推断为空。这似乎是由于: 。。。未以其他方式推导的尾部模板参数包([temp.var
模板结构{使用type=int;};
模板
void f(typename S::type){
静态断言(sizeof…(T)==0);
}
模板
void g(typename S::type,S*){}
int main(){
f(42);
g(42,nullptr);
}
GCC和Clang都对调用f
感到满意,但对g
的调用并不满意
在对f
的调用中,虽然T.
出现在非推断上下文中,但最终被推断为空。这似乎是由于:
。。。未以其他方式推导的尾部模板参数包([temp.variadic])将被推导为模板参数的空序列
然而,在对g
的调用中,T..
额外出现在推导的上下文中,这会导致尝试推导并失败,这似乎会导致g
变得不可行。似乎没有“回退”到T..。
在尝试扣除失败后为空
- 这种行为是故意的吗?若然,原因为何
- 如果是这样,是否打算用“不以其他方式推断”的措词来具体说明这种行为?(也就是说,它意味着只有当包出现在没有推断的上下文中时,才会出现空回退)
- 如果是的话,这个措辞是否足够清楚?“未以其他方式推断”的另一种解释似乎是“要么未进行推断,要么试图进行推断但失败”
template<class T>
void f(T, T){};
int main() {
f(int{42},short{42});
}
或者,如果你愿意,只要
intmain(){
s1;
g(&s1,0);
}
而两者的失败原因相同
现在,如果你想允许转换-然后使用一个身份模板-这甚至适用于非推断上下文
对于您的情况,示例可能类似于():
模板
void g(typename S::type,std::type_identity_t*){}
int main(){
f(42);
g(42,nullptr);
}
这是有效的。(注意,如果您不需要,只需自己编写身份模板)
如a中所述,将问题转过来可能会引出一个更有趣的问题
在非推导上下文中允许空模板参数推断的原因是什么?我认为这是有意的,因为我想不出可能的替代阅读。完全没有模板:
void f(int*){}void f(double*){}void g(){f(nullptr);}
——在我看来是一致的(在模板产生的许多重载中,我们实际希望使用哪一个?).我宁愿把问题转过来:在非推断上下文中允许空模板参数推断的原因是什么?@Aconcagua可能与默认模板参数在非推断上下文中使用的原因相同@Rakete1111,因此空参数列表将被视为“默认”?似乎有点合理…@Brian Itnullptr_t是类型推断系统的死胡同。它无法从nullptr转到“指向某种S的指针”。作为一种可能的解决方法,您可以提供一个提示:模板S*sp_conv(nullptr_t zp){return zp;};然后调用g(42,sp_conv(nullptr));这适用于GCC v8和Clang v7。听起来您的观点是,只有在给定模板参数(或模板参数包)的每个推导上下文中的推导成功时,整体推导才会成功;如果同一模板参数有两个推导的上下文,并且其中一个上下文的推导失败,那么该参数就不会根据另一个上下文进行推导;相反,推导就失败了。但是,我认为你没有回答我的问题。一个人不能读到“不以其他方式推导”吗关于这种情况?从某种意义上说,这本书没有被演绎,因为演绎失败了。如果在真空中阅读,我想你是对的,它有点不清楚。也许我有预感,但我确实读到了“没有其他方式演绎”!=“演绎尝试过并失败了”和“没有其他方式演绎”=“演绎实际上没有完成”。这主要是因为其中的“否则”。这还不清楚,但可能还有改进的余地。@darune我想问标准为什么会使用主要是英国的“其他方式”(意思是“以其他方式”)
template<class T>
void f(T, T){};
int main() {
f(int{42},short{42});
}
template <class... T>
void g(S<T...>*, S<T...>* ) {}
int main() {
S<> s1;
g(&s1, nullptr);
}
int main() {
S<> s1;
g(&s1, 0);
}
template <class... T>
void g(typename S<T...>::type, std::type_identity_t<S<T...> >*) {}
int main() {
f(42);
g(42, nullptr);
}