#include <type_traits>
template<typename T>
void f(T&& a, T&& b)
{}
int main()
{
int n;
f(n, std::move(n));
}
T&&
是转发引用类型,因此我认为decltype(a)
应该是int&
而decltype(b)
应该是int&&
。
但是,上面的代码会生成以下错误:
main.cpp(13,2):错误:没有匹配函数来调用'f' f(n,std :: move(n));
main.cpp(7,6):注意:候选模板被忽略:推导出参数'T'的冲突类型('int&amp;'与'int')
void f(T&amp; a,T&amp;&amp; b)
生成了1个错误。
当lvalue-ref-arg和rvalue-ref-arg作为相同的forwarding-ref类型传递时,为什么它不起作用?
我的编译器是clang 4.0。
答案 0 :(得分:4)
编译错误非常简单,T
和int&
无法同时推断int
。为了使其工作,您必须提供额外的模板参数:
template<typename T1, typename T2>
void f(T1&& a, T2&& b)
{}
模板参数推导在处理基于forwarding references
的reference collapsing rules(a.k.a通用引用)时遵循特殊规则例如,如果您有一个功能模板:
template<typename T>
void foo(T&&) {}
foo
输入是U
类型的左值,T
将被扣除到U&
。遵循参数折叠规则,参数类型将变为U&
。foo
输入是U
类型的右值,T
将被扣除到U
,因此参数类型将为U&&
。现在按照上面的推理,模板参数推导会将T
推导出int&
作为第一个参数,因为输入是lvalue
。
模板参数推导将尝试匹配第二个参数的类型,但对于第二个参数,因为输入是遵循上述T
规则的右值,将推导到int
。
此时,编译器向空中举手并尖叫“ dude T
的扣除类型必须与所有输入参数匹配”。
答案 1 :(得分:2)
当使用转发参考(1)工作到非表面深度时,重要的是要了解它们的实际工作方式,而不是将它们视为“神奇”。
转发参考的所有“魔力”都是标准中的这一裁决:
当参数类型T &&
发生类型推导时,其中T
是推导类型,并且U
类型的左值被用作参数,类型U &
是用于类型推导而不是U
。
我们来看这个例子:
template <class T>
void foo(T&& a);
int i = 42;
致电foo(42)
时会发生什么:42
是int
类型的左值。因此,T
将推断为int
,a
的类型将为int &&
,这是对int
的右值引用。
致电foo(i)
时会发生什么:i
是int
类型的左值。由于转发参考规则,将发生扣除,就好像int &
是类型一样。因此T
推导为int &
;因此,a
的类型为“int & &&
”,该引用会折叠为int &
。
考虑到这一点,很明显为什么你的例子失败了。同样的T
必须推断为int &
(因为n
)和int
(因为std::move(n)
)。很自然地,演绎失败了。
如果您希望每个参数都是自己的转发参考,则每个参数需要一个单独的模板参数:
template<class T, class U>
void f(T&& a, U&& b)
{}
(1)请注意,根据已被工作文件接受的提案N4164,首选术语是转发参考。