-
左值(lvalue)是指可以取地址的表达式
-
右值(rvalue)是指临时的、不可取地址的表达式;可以通过使用双 ampersand (&&) 作为引用类型来实现。
- 右值引用允许
将临时对象(右值)绑定到引用上
,并在方法中修改它们。C++ 的语言规范,对于右值引用绑定的临时对象,其生命周期通常在方法退出后结束
,因此在方法退出后,对这个临时对象的修改可能会导致未定义行为。 - 右值引用的设计初衷是用于优化性能,例如通过避免
不必要的对象复制
- 要避免对右值引用绑定的临时对象进行修改后导致未定义行为,
可以使用 std::move 函数将右值引用转换为左值引用
,从而使得在方法中修改的是原始对象而不是临时对象。
- 右值引用允许
-
对于方法来说,传递左值或右值作为参数各有不同的用途和行为:
- 传递左值:
将左值传递给方法,方法可以对其进行修改,并且对传递的左值的修改会在函数调用后保持
。这对于需要在方法内部修改传递的对象,并且希望在函数调用后对原对象产生影响的情况非常有用。 - 传递右值:将右值传递给方法,方法可以从其获取值或者资源,并在方法内部进行处理,例如移动语义(move semantics)的情况下,可以将右值的资源转移到方法内部,从而避免不必要的复制操作。传递右值还可以用于实现完美转发(perfect forwarding),将右值引用传递给下游函数。
- 传递左值:
在一般情况下,对于方法的入参:
如果方法需要修改传递的对象或者希望在函数调用后对原对象产生影响,则传递左值作为参数。
如果方法只需要获取传递的值或者资源,并且不需要修改传递的对象
,或者希望实现移动语义或完美转发,则传递右值作为参数。这可以根据方法的具体需求和语义来进行选择。
需要注意的是,在 C++11 及之后的版本中,通过使用引用折叠规则(reference collapsing rules)和右值引用(rvalue reference),可以实现在方法中同时接收左值和右值的参数,从而在函数调用时不需要显式地区分左值和右值。这可以提供更加灵活和高效的参数传递方式。
void modifyLeftValue(int& x) {
x = x + 1; // 修改传递的左值
}
int main(){
int num = 10;
modifyLeftValue(num); // 传递左值给方法
std::cout << num << std::endl; // 输出 11,因为方法内部修改了传递的左值
return 0;
}
void processRightValue(std::string&& str) {
// Processing: Hello , str : 0x308c32280
std::cout << "Processing: " << str <<" , str : "<<&str<< std::endl; // 使用传递的右值
}
int main(){
processRightValue("Hello"); // 传递右值给方法
// 注意:传递的右值字符串在方法调用后会被销毁,因为它是临时的,不可取地址的表达式
return 0;
}
//
void processRightValue(std::string&& str) {
std::cout << "Processing: " << str <<" , str : "<<&str<< std::endl;
str = "xxx";
}
int main(){
std::string val = "hello";
//std::move(val) 和 std::forward<std::string>(val) 传递进去的值被修改的调用后 都能保留
processRightValue(std::move(val));
std::cout<<"--val : "<<val<<std::endl;//xxx
return 0;
}