2
#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)的,我们正在试图实例F1与类型参数T为INT & &,所以输入参数T & &为int & & & &,然后减小到int & &。换句话说,我们有:

void f1<int &&>(int &&); 

这意味着此实例化与函数f2中的完全相同,对吗?那么为什么f1有效而f2不能呢?

+0

'T'不是'int &&'。 'T'是'int&'。哪个崩溃到'int&'。见[this](http://coliru.stacked-crooked.com/a/76666156bcf580f4)。 – nwp

回答

2

那么为什么第1行工作?

在模板参数推导中有一个特殊规则,它被引入允许完美转发。在模板参数推导的上下文中,T&&不是右值引用,而是转发引用

如果左值被传递给服用转发参考函数模板,类型参数推导出作为T&代替T。这允许参考折叠发生:T& &&变成T&

cppreference

如果P是一个rvalue参照CV-不合格模板参数(所谓的转发参考),以及相应的函数调用参数是一个左值,类型左值参照(注意:这是std :: forward动作的基础)注意:在类模板参数推导中,类模板的模板参数永远不是转发引用(自C++ 17以来))

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 
} 

在第2行中没有进行模板参数推演 - f2需要右参考,并会拒绝任何不会绑定到该参数的东西。 左值不绑定到右值引用

+0

让我简单回顾一下我做错了什么。当我们试图推导模板类型参数T时,我们应该将输入视为一个表达式,即使它也可能是一个变量。作为变量,x的类型为** int && **(即右值引用),但作为表达式,x的类型为int,并且是左值。现在,根据你刚刚提到的_special rule_,当我们调用f1(x)时,编译器将T的类型推断为** int&**,而不是** int **(也不是** int && **)。最后,根据引用崩溃规则,我们将** int &&& **简化为** int&**。我这次是对的吗? – ivy

+1

@ivy:听起来对我很正确。这是'int&'扣除的经验证明:https://wandbox.org/permlink/vIEe4WtqkVtMRtlF –