为什么参考折叠规则仅适用于模板?
#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不能呢?
那么为什么第1行工作?
在模板参数推导中有一个特殊规则,它被引入允许完美转发。在模板参数推导的上下文中,T&&
不是右值引用,而是转发引用。
如果左值被传递给服用转发参考函数模板,类型参数推导出作为T&
代替T
。这允许参考折叠发生:T& &&
变成T&
。
如果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
需要右参考,并会拒绝任何不会绑定到该参数的东西。 左值不绑定到右值引用。
让我简单回顾一下我做错了什么。当我们试图推导模板类型参数T时,我们应该将输入视为一个表达式,即使它也可能是一个变量。作为变量,x的类型为** int && **(即右值引用),但作为表达式,x的类型为int,并且是左值。现在,根据你刚刚提到的_special rule_,当我们调用f1(x)时,编译器将T的类型推断为** int&**,而不是** int **(也不是** int && **)。最后,根据引用崩溃规则,我们将** int &&& **简化为** int&**。我这次是对的吗? – ivy
@ivy:听起来对我很正确。这是'int&'扣除的经验证明:https://wandbox.org/permlink/vIEe4WtqkVtMRtlF –
'T'不是'int &&'。 'T'是'int&'。哪个崩溃到'int&'。见[this](http://coliru.stacked-crooked.com/a/76666156bcf580f4)。 – nwp