#include <iostream>
template <typename T> void f1(T&& r1){
std::cout<<r1;
}
void f2(int&& r2){
std::cout<<r2;
}
int main() {
int&& x = 42;
f1(x); //line 1: No error here. Why?
f2(x);//line2: Error here. why?
}
我认为我理解为什么第2行会出现错误。变量x是右值参考到int 42并被视为表达式,x是左值。在函数f2中,输入r2是右值参考,因此只能绑定到右值,因此我们有错误。
现在,我的问题是,为什么函数f1中看似等效的代码运行得很好?我知道这可能与参考折叠规则有关,即,当我们执行f1(x)时,我们试图用类型参数T为int&amp;&amp;来实例化f1,因此输入参数T&amp;&amp;是int&amp;&amp; &amp;&amp;,然后减少到int&amp;&amp ;.换句话说,我们有:
void f1<int &&>(int &&);
这意味着这个实例化与函数f2完全相同,对吗?那么为什么f1可以工作而f2不工作呢?
答案 0 :(得分:2)
为什么第1行有效?
模板参数推断中有一条特殊规则,用于允许完美转发。在模板参数推导的上下文中,<transaction>
<record type="1" >
<field number="1" >
<item >223</item>
</field>
</record>
<record type="14" >
<field number="1" >
<item >777</item>
</field>
</record>
<record type="14" >
<field number="1" >
<item >555</item>
</field>
</record>
<record type="200" >
<field number="1" >
<item>546</item>
</field>
</record>
<record type="201" >
<field number="1" >
<item>123</item>
</field>
</record>
</transaction>
不是右值引用,而是转发引用。
如果将左值传递给采用转发参考的函数模板,则类型参数将推导为T&&
而不是T&
。这样就可以进行引用折叠:T
变为T& &&
。
来自cppreference:
如果P是对cv非限定模板参数(所谓的转发引用)的右值引用,并且相应的函数调用参数是左值,则使用对A的类型左值引用代替A进行推导(注意:这是std :: forward动作的基础注意:在类模板参数推导中,类模板的模板参数永远不是转发引用(因为C ++ 17))
T&
在第2行中,没有模板参数推断 - template<class T>
int f(T&&); // P is an rvalue reference to cv-unqualified T (forwarding reference)
template<class T>
int g(const T&&); // P is an rvalue reference to cv-qualified T (not special)
int main()
{
int i;
int n1 = f(i); // argument is lvalue: calls f<int&>(int&) (special case)
int n2 = f(0); // argument is not lvalue: calls f<int>(int&&)
// int n3 = g(i); // error: deduces to g<int>(const int&&), which
// cannot bind an rvalue reference to an lvalue
}
采用右值引用,并将拒绝任何不与之绑定的内容。 Lvalues 不会绑定到右值引用。