我仍然对为支持移动和转发而发明的规则感到困惑。我仍然不确定的一件事是:
转发引用只是右值引用(应用了引用折叠规则)吗?
如果它是一个右值引用,那么为什么该函数:
template<typename T>
void func(T&&);
不仅接受右值,还接受左值?
我仍然对为支持移动和转发而发明的规则感到困惑。我仍然不确定的一件事是:
转发引用只是右值引用(应用了引用折叠规则)吗?
如果它是一个右值引用,那么为什么该函数:
template<typename T>
void func(T&&);
不仅接受右值,还接受左值?
BeforeT
被替换,T &&
是一个右值引用(显然)。
AfterT
被替换(并且在引用折叠之后),T &&
要么保持右值引用(如果T
不是引用),要么成为左值引用(如果T
是左值引用)。
我不确定这个答案是否会让你满意,但我可以指出标准的相关部分。简而言之,引用T&&
“在语法上”始终是右值引用,但有时它最终声明的类型是左值引用类型。
当由于模板参数推导而发生这种情况时,整个构造称为“转发引用”,作为一种方便的简写。(这种情况需要引用折叠,但模板参数推导并不是唯一发生引用折叠的情况。)
现在,进入标准措辞。首先我们有 [dcl.ref] (例如p2,p6):
使用声明的引用类型
&
称为左值引用,使用声明的引用类型&&
称为右值引用。[...]如果typedef-name (9.1.3, 13.1) 或decltype-specifier (9.1.7.2) 表示类型
TR
是对 type 的引用T
,则尝试创建类型“lvalue reference to cvTR
”会创建类型“lvalue引用T
”,而尝试创建类型“对cvTR
的右值引用”会创建类型TR
。[注意:此规则称为参考折叠。——尾注]
最后,模板参数推导的情况在 [temp.deduct.call] p3中处理:
转发引用是对 cv 非限定模板参数的右值引用 [... ]
换句话说,转发引用是一个右值引用,但它也接受左值。(请注意,标准对“转发引用”的定义实际上并不需要推导模板参数,尽管这是您通常希望触发引用折叠行为的主要方式。)