右值引用(Rvalue Reference)是 C++11 引入的关键特性,用于实现高效资源转移和完美转发。
1. 左值 vs 右值
左值 (Lvalue)
-
特点:有持久状态、可取地址的表达式
-
示例:
int a = 10; // a 是左值 std::string s = "hi"; // s 是左值
右值 (Rvalue)
-
特点:临时对象、字面量、即将销毁的值
-
示例:
42; // 字面量是右值 std::string(); // 临时对象是右值 a + b; // 表达式结果是右值
2. 右值引用的语法
-
符号:
&&(注意与通用引用区分) -
声明示例:
int&& rref = 42; // 右值引用绑定到字面量 std::string&& sref = get_temp_string(); // 绑定到临时对象
3. 核心用途
(1) 移动语义(Move Semantics)
问题场景:深拷贝临时对象效率低下
std::vector<int> create_big_vector(); // 返回临时对象
std::vector<int> v = create_big_vector();
// 传统拷贝:需要复制所有元素(低效)
解决方案:通过右值引用"窃取"资源
class Vector {
public:
// 移动构造函数
Vector(Vector&& other) noexcept
: data_(other.data_), size_(other.size_) {
other.data_ = nullptr; // 使原对象处于可析构状态
}
private:
int* data_;
size_t size_;
};
使用效果:
Vector v1;
Vector v2 = std::move(v1); // 调用移动构造,仅转移指针
(2) 完美转发(Perfect Forwarding)
问题场景:模板函数参数传递时丢失左右值属性
template<typename T>
void wrapper(T arg) {
func(arg); // 无论传入左值右值,arg都是左值
}
解决方案:std::forward + 右值引用
template<typename T>
void wrapper(T&& arg) { // 通用引用
func(std::forward<T>(arg)); // 保持原始值类别
}
使用示例:
wrapper(42); // 转发右值
int x = 10;
wrapper(x); // 转发左值
4. 关键函数与操作
| 工具 | 作用 |
|---|---|
std::move() | 将左值强制转换为右值引用,启用移动语义 |
std::forward<T>() | 保持参数原始值类别(用于完美转发) |
noexcept | 声明移动操作不抛异常(STL容器在重新分配时会优先使用noexcept移动操作) |
5. 实际应用场景
场景1:优化容器操作
std::vector<std::string> merge(
std::vector<std::string>&& a,
std::vector<std::string>&& b) {
std::vector<std::string> result = std::move(a);
result.insert(result.end(),
std::make_move_iterator(b.begin()),
std::make_move_iterator(b.end()));
return result; // 触发返回值优化(RVO)
}
场景2:实现资源管理类
class FileHandle {
public:
FileHandle(FileHandle&& other) noexcept
: handle_(other.handle_) {
other.handle_ = nullptr;
}
~FileHandle() {
if (handle_) close(handle_);
}
private:
FILE* handle_;
};
6. 注意事项
-
不要滥用
std::move:std::string s = "hello"; std::string t = std::move(s); // s现在处于有效但未定义状态(可能为空) -
移动后对象应处于可析构状态:
class Resource { Resource(Resource&& other) { ptr_ = other.ptr_; other.ptr_ = nullptr; // 必须置空! } int* ptr_; }; -
区分通用引用和右值引用:
template<typename T> void foo(T&& param); // 通用引用(可能是左值或右值) void bar(int&& param); // 纯右值引用
为什么右值引用重要?
-
性能提升:减少不必要的拷贝(特别是处理容器、字符串等资源密集型对象)
-
实现资源所有权转移:如
unique_ptr的核心机制 -
支持完美转发:使模板函数能保持参数的原始值类别
822

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



