为什么参考折叠规则仅适用于模板?

时间:2017-10-16 10:05:01

标签: c++11 templates parameter-passing pass-by-reference rvalue-reference

#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不工作呢?

1 个答案:

答案 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 不会绑定到右值引用