Bri*_*ian 14 c++ templates language-lawyer variadic-templates
考虑以下示例(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);
}
Run Code Online (Sandbox Code Playgroud)
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});
}
Run Code Online (Sandbox Code Playgroud)
以上无法编译。为什么?因为即使无缝short转换int(升级),它也不是同一类型。
另外,由于nullptr只是具有某种简单的类型- 它根本std::nullptr_t不适合参与模板参数推导。
因此,让我们暂时忘记非推导的上下文,并尝试使用推导的上下文:
template <class... T>
void g(S<T...>*, S<T...>* ) {}
int main() {
S<> s1;
g(&s1, nullptr);
}
Run Code Online (Sandbox Code Playgroud)
或者如果您愿意,只需
int main() {
S<> s1;
g(&s1, 0);
}
Run Code Online (Sandbox Code Playgroud)
两者都因同样的原因而失败。
现在,如果您想允许转换 - 则使用身份模板 - 这甚至适用于非推导的上下文!
对于您的情况,示例可能如下所示(c++2a):
template <class... T>
void g(typename S<T...>::type, std::type_identity_t<S<T...> >*) {}
int main() {
f(42);
g(42, nullptr);
}
Run Code Online (Sandbox Code Playgroud)
这是有效的。(请注意,如果您没有c++20,只需自己编写身份模板)
正如评论中所述,扭转问题也许会引出一个更有趣的问题?
在非推导上下文中允许空模板参数推导的原因是什么?
| 归档时间: |
|
| 查看次数: |
197 次 |
| 最近记录: |