我们先介绍一个表示参数包的辅助类型:
template<typename... T> struct Pack { };
现在,这是具有奇怪行为的函数:
template<typename... TT, typename T>
void f(Pack<TT...>, Pack<T>, std::type_identity_t<TT>..., std::type_identity_t<T>);
std::type_identity_t
此处用于禁用对最后两个参数的推导,以防引入任何歧义。
首先,我试着这样称呼它:
f(Pack<int>{}, Pack<int>{}, 5, 5);
GCC 引发错误并给出以下解释:
<source>:12:6: note: candidate expects 3 arguments, 4 provided
12 | f(Pack<int>{}, Pack<int>{}, 5, 5);
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这不是我预期的那种错误,我认为扣除会导致T = int
and TT... = int
。但是,好吧,让我们按照笔记所说的做,并少提供一个论点:
f(Pack<int>{}, Pack<int>{}, 5);
它仍然给出一个错误,但这次是:
<source>:12:6: error: too few arguments to function 'void f(Pack<TT ...>, Pack<T>, std::type_identity_t<TT>..., std::type_identity_t<T>) [with TT = {int}; T = int; std::type_identity_t<T> = int]'
10 | f(Pack<int>{}, Pack<int>{}, 5);
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
现在他们太少了。另请注意,此错误消息证实了我对推导的T
和的假设TT...
。
此时我切换到 Clang 以查看它是否会编译此代码,但上述两个调用f
(带有 3 个和 4 个参数)都会导致相同的错误,并注意:
<source>:8:6: note: candidate template ignored: deduced packs of different lengths for parameter 'TT' (<int> vs. <>)
void f(Pack<TT...>, Pack<T>, std::type_identity_t<TT>..., std::type_identity_t<T>);
^
我还尝试使用 MSVC 编译代码,它确实编译没有错误。
到底是怎么回事?有什么东西使这段代码无效吗?由于奇怪的错误消息,它是编译器错误吗?
std::type_identity<TT>...
在评论中提到作为从参数推导中删除第三个函数参数(这是一个包)的一种方法,但这没有效果,因为没有出现在参数列表末尾的函数参数包位于非无论如何推断上下文;根据[temp.deduct.type]/5.7:
/5 未推断的上下文是:
- [...]
- /5.7 不出现在参数声明列表末尾的函数参数包。
那么为什么不能从第一个函数参数(Pack<TT...>
)中明确推导出参数包呢?这对于 C++ 开发人员来说可能是有意义的,但是它可能最终成为实现者的实现质量问题,因为它将开始将模板参数推导与重载解析混合在一起。这将需要两次模板参数推导,其中首先明确推导出一个参数包,然后从一个函数参数中推导出,然后使用结果修改同一函数的参数列表(以扩展其他不可推导的参数包),然后返回到修改后的函数模板的模板参数推导。这不是模板参数推导在其他任何地方的工作方式,可能仅仅是由于实现的质量。
[temp.deduct.call]/1确实提到从未推断出这样的包(强调我的):
[...]当函数参数包出现在非推导上下文([temp.deduct.type])中时,永远不会推导该包的类型。
可以争论这是否故意拒绝可以从其他任何地方推断出来的包。如果是这样,则 OP 的程序确实格式错误,但是编译器的错误消息对于将其诊断为根本原因(如果是的话)并没有多大帮助。似乎编译器可能依赖 [temp.deduct.call]/1 将不可演绎的模板参数包实际推导出为空包。
我们最终可能会注意到 [temp.deduct.call]/1 支持使用显式模板参数,因为这不是推导:
#include <type_traits>
template<typename... T> struct Pack { };
// Note the swap of template argument positions
// to allow explicitly providing template arguments.
template<typename T, typename... TT>
void f(Pack<TT...>, Pack<T>, std::type_identity_t<TT>..., std::type_identity_t<T>) {}
int main() {
f<int, int, int>(Pack<int, int>{}, Pack<int>{}, 1, 2, 3);
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句