为什么引用折叠规则仅适用于模板?
Why reference collapsing rules work only for templates?
#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 是 rvalue reference 到 int 42 并且被认为是一个表达式,x 是一个 左值。在函数 f2 中,输入 r2 是一个 rvalue reference,因此只能绑定到一个 rvalue,所以我们有一个错误。
现在,我的问题是,为什么函数 f1 中看似等效的代码工作得很好?我知道这可能与引用折叠规则有关,即当我们执行 f1(x) 时,我们试图用类型参数 T 为 int && 来实例化 f1,因此输入参数 T&& 为 int&& &&,然后减少到 int &&。换句话说,我们有:
void f1<int &&>(int &&);
这意味着这个实例化与函数 f2 中的完全相同,对吧?那么为什么 f1 有效而 f2 无效?
So why does line 1 works?
模板参数推导中有一个特殊规则被引入以允许完美转发。在模板参数推导的上下文中,T&&
不是 右值引用 ,而是 转发引用 。
如果将 lvalue 传递给采用 转发引用 的函数模板,则类型参数被推断为 T&
而不是 T
。这允许 reference collapsing 发生:T& &&
变成 T&
.
来自cppreference:
If P is an rvalue reference to a cv-unqualified template parameter (so-called forwarding reference), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Note: this is the basis for the action of std::forward Note: in class template argument deduction, template parameter of a class template is never a forwarding reference (since 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
采用 右值引用 ,并将拒绝任何不绑定到它的内容。 左值不绑定到右值引用。
#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 是 rvalue reference 到 int 42 并且被认为是一个表达式,x 是一个 左值。在函数 f2 中,输入 r2 是一个 rvalue reference,因此只能绑定到一个 rvalue,所以我们有一个错误。
现在,我的问题是,为什么函数 f1 中看似等效的代码工作得很好?我知道这可能与引用折叠规则有关,即当我们执行 f1(x) 时,我们试图用类型参数 T 为 int && 来实例化 f1,因此输入参数 T&& 为 int&& &&,然后减少到 int &&。换句话说,我们有:
void f1<int &&>(int &&);
这意味着这个实例化与函数 f2 中的完全相同,对吧?那么为什么 f1 有效而 f2 无效?
So why does line 1 works?
模板参数推导中有一个特殊规则被引入以允许完美转发。在模板参数推导的上下文中,T&&
不是 右值引用 ,而是 转发引用 。
如果将 lvalue 传递给采用 转发引用 的函数模板,则类型参数被推断为 T&
而不是 T
。这允许 reference collapsing 发生:T& &&
变成 T&
.
来自cppreference:
If P is an rvalue reference to a cv-unqualified template parameter (so-called forwarding reference), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Note: this is the basis for the action of std::forward Note: in class template argument deduction, template parameter of a class template is never a forwarding reference (since 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
采用 右值引用 ,并将拒绝任何不绑定到它的内容。 左值不绑定到右值引用。