为何返回右值引用的表达式无法初始化非const左值引用?
关于右值引用作为函数返回值的值类别疑惑解答
嘿,这个问题其实戳中了C++里最容易混淆的点之一:引用类型和表达式的值类别完全是两码事!很多人刚接触右值引用时都会在这里绕晕,我来给你拆解清楚。
先直接回答你的两个核心问题
- 示例1中的
fun()表达式是右值(更准确地说是「亡值xvalue」,属于右值的范畴) - 示例1中的
fun()的返回类型确实是右值引用int&&
这两个结论并不矛盾,因为「返回的引用类型」和「函数调用表达式的值类别」是两个独立的概念,我们逐个看你的示例就能明白:
示例1:返回右值引用的函数调用为何是右值?
先看代码:
int &&fun() { return 1; } int main() { int &a = fun(); } // 编译报错
C++标准有明确规则:
- 如果函数返回左值引用
T&,那么函数调用表达式是左值 - 如果函数返回右值引用
T&&,那么函数调用表达式是xvalue(亡值)——而亡值属于右值的一种 - 如果函数返回非引用类型
T,函数调用表达式是prvalue(纯右值),也是右值
而左值引用int&只能绑定到左值,不能直接绑定右值(除非是const int&,它可以绑定所有值类别),所以编译器报错“无法用int右值初始化int&”。
示例2:返回左值引用的函数调用是左值
代码:
int & fun(){ int b; return b; } int main(){ int & a=fun(); } // 编译通过(仅警告)
这里fun()返回左值引用,所以函数调用表达式是左值,左值引用int&自然可以绑定到左值。当然,返回局部变量的引用是未定义行为,编译器只会给警告,但语法上是允许的。
示例3:decltype保留返回的引用类型
代码:
int &&fun() { return 1; } int main() { decltype(fun()) b = 1; } // 编译成功
decltype的规则是:对于函数调用表达式,如果函数返回引用类型,decltype(fun())会直接保留这个引用类型(这里就是int&&),所以你可以用它初始化右值引用变量,这只能证明函数的返回类型是右值引用,和fun()表达式本身的值类别无关。
示例4:具名的右值引用是左值
代码:
int &&a = 1; int &b = a; // 编译通过
这里的a是具名的右值引用变量——注意:所有具名的变量(不管是左值引用、右值引用还是普通变量)都是左值!所以a是左值,左值引用int&当然可以绑定到它。这和示例1完全不矛盾,因为示例1中的fun()是函数调用表达式(xvalue,右值),而示例4中的a是具名变量(左值)。
最后总结几个关键规则,帮你理清逻辑
- 具名引用都是左值:不管是
int&还是int&&类型的变量,只要有名字,它就是左值 - 函数返回值的类型决定表达式的值类别:
- 返回
T&→ 函数调用是左值 - 返回
T&&→ 函数调用是xvalue(右值) - 返回非引用
T→ 函数调用是prvalue(右值)
- 返回
- 左值引用的绑定规则:
T&只能绑定左值,const T&可以绑定左值、右值 - 右值引用的绑定规则:
T&&可以绑定右值,也可以通过std::move绑定左值(把左值转成右值)
内容的提问来源于stack exchange,提问作者namasikanam
相关产品推荐
相关产品推荐

