在 C++ 中,右值引用 (T&&) 主要用于实现 移动语义 和 完美转发,可延长临时对象的生命周期,减少深拷贝的工作等。以下是几个实际应用场景和代码示例:
核心概念
左值 (lvalue)
-
有持久身份(可通过地址访问)
-
可出现在赋值运算符左侧(可修改的左值)
-
通常对应变量或具名对象
右值 (rvalue)
-
临时对象(用完即销毁)
-
不能出现在赋值运算符左侧
-
包括字面量、临时值和纯计算结果
左值右值示例
#include <iostream>
using namespace std;
int main() {
// 1. 基础左值 vs 右值
int a = 10; // a 是左值 (有内存地址), 10 是右值 (字面量)
int b = a; // b 是左值, a 作为右值使用 (读取其值)
// 10 = a; // 错误!右值 10 不能放在赋值左侧
// 2. 函数返回值的左右值属性
auto getInt = []() { return 42; };
int c = getInt(); // getInt() 返回右值 (临时值)
// getInt() = 100; // 错误!函数返回的右值不可赋值
// 3. 左值引用 vs 右值引用
int& lref = a; // 左值引用 ✔️ (绑定到左值)
// int& lref2 = 10; // 错误!左值引用不能绑定右值
int&& rref = 10; // 右值引用 ✔️ (绑定到右值)
int&& rref2 = getInt(); // 绑定函数返回的右值
// 4. 字符串字面量的特殊性
const char* s1 = "Hello"; // "Hello" 是左值(有固定地址)
// char* s2 = "World"; // 错误!字符串字面量不可修改
// 5. 运算符的左右值示例
int arr[5] = {};
arr[2] = 5; // arr[2] 是左值 (可修改)
int* p = &arr[0]; // & 取地址操作要求操作数是左值
// 6. 表达式结果类型
int x = 1, y = 2;
x + y = 4; // 错误!(x+y) 结果是右值(临时值)
(x > y ? x : y) = 10; // 正确:三目运算符返回左值引用
// 7. 移动语义示例(C++11)
string s1 = "Old";
string s2 = move(s1); // move(s1) 将左值 s1 转为右值
cout << s1; // s1 变为空(资源被移动)
}
右值引用的几个主要应用场景
1. 移动构造函数 & 移动赋值运算符
目的:高效转移资源所有权(如动态内存),避免深拷贝
class String {
public:
char* data;
size_t size;
// 移动构造函数
String(String&& other) noexcept
: data(other.data), size(other.size) {
other.data = nullptr; // 置空源对象,防止双重释放
other.size = 0;
}
// 移动赋值运算符
String& operator=(String&& other) noexcept {
if (this != &other) {
delete[] data; // 释放当前资源
data = other.data; // 接管资源
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
// 传统拷贝构造/赋值(对比用)
String(const String&) = delete; // 禁用拷贝
String& operator=(const String&) = delete;
};
- 使用场景:
String createString() {
String temp("Hello");
return temp; // 触发移动构造(非拷贝)
}
int main() {
String s1 = createString(); // 移动构造
String s2 = std::move(s1); // 显式移动
}
2. 标准库中的移动优化
目的:容器操作避免不必要的拷贝
#include <vector>
#include <string>
int main() {
std::vector<std::string> v;
std::string s = "Large string";
v.push_back(std::move(s)); // 移动而非拷贝
// s 变为空(但有效状态)
std::cout << "s after move: " << s; // 输出空
}
3. 完美转发 (Perfect Forwarding)
目的:保持参数原始类型(左值/右值)传递给下层函数
#include <utility>
// 转发函数模板
template <typename Func, typename... Args>
auto wrapper(Func&& func, Args&&... args) {
// 保持 args 的值类别(左值/右值)
return std::forward<Func>(func)(
std::forward<Args>(args)...
);
}
void process(int& x) { std::cout << "lvalue\n"; }
void process(int&& x) { std::cout << "rvalue\n"; }
int main() {
int a = 10;
wrapper(process, a); // 调用左值版本
wrapper(process, 20); // 调用右值版本
wrapper(process, std::move(a)); // 调用右值版本
}
4. 工厂函数返回独占资源
目的:安全返回不可拷贝的资源
#include <memory>
std::unique_ptr<int> createResource(int value) {
auto res = std::make_unique<int>(value);
return res; // 移动构造 unique_ptr
}
int main() {
auto ptr = createResource(42); // 无拷贝开销
// ptr 独占资源
}
5. 优化临时对象处理
目的:识别临时对象并优化操作
class Matrix {
public:
// 重载加法运算符
Matrix operator+(Matrix&& other) { // 当右侧是临时对象时
// 直接复用 other 的存储
other.data = nullptr;
return std::move(*this);
}
};
6. std::move 强制转换
目的:显式标记不再需要的对象
void consumeBigData(BigData&& data); // 只接受右值
int main() {
BigData data;
// ... 使用 data ...
consumeBigData(std::move(data)); // 显式放弃所有权
// 此处不应再使用 data(处于有效但未定义状态)
}
992

被折叠的 条评论
为什么被折叠?



