三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

std::move

std::move

std::move是 C++11 引入的移动语义核心工具

1.功能

std::move不移动任何东西,它是一个类型转换工具,将左值(有名字的变量)无条件转换为右值引用(T&&,从而“骗过”编译器,去调用移动构造函数或移动赋值运算符。

  • 目的:转移资源所有权(如堆内存、文件句柄),避免深拷贝的开销。

  • 效果:被移动的对象进入“有效但未指定”的状态(通常是空),不应再访问其内容(除非重新赋值)。


2. 基础用法

2.1 移动构造函数(转移堆内存)

class Buffer { int* ptr; public: // 移动构造函数(noexcept 是关键优化提示) Buffer(Buffer&& other) noexcept : ptr(other.ptr) { other.ptr = nullptr; // 防止析构时 double free } }; Buffer a; Buffer b = std::move(a); // 触发移动,a 变为空指针

noexcept是 C++11 引入的异常规范修饰符,核心作用:标记函数不抛异常编译器优化提示

2.2移动赋值运算符(转移并清理旧资源)

Buffer& operator=(Buffer&& other) noexcept { if (this != &other) { delete ptr; // 释放自己的旧资源 ptr = other.ptr; // 接管别人资源 other.ptr = nullptr; } return *this; } b = std::move(a); // 触发移动赋值

2.3 STL 容器的高效操作(最实用)

std::vector<std::string> v1 = {"a", "b"}; std::vector<std::string> v2 = std::move(v1); // O(1) 转移指针,不拷贝元素 // 向容器插入大对象,避免拷贝 std::vector<MyClass> vec; MyClass obj; vec.push_back(std::move(obj)); // 直接转移,不复制

3. 注意事项

  • 1:std::move若要用原变量,必须重新赋值后才安全。移动后访问是未定义行为(通常崩溃)。

    std::string s = "hello"; std::string t = std::move(s); std::cout << s; // 未定义行为!s 可能为空或乱码 s = "new"; // 重新赋值后可安全使用
  • 2:只有定义了移动构造函数的类型才高效移动(如std::vector)。简单类型(int)拷贝和移动无区别。

  • 3:const对象用std::move
    const std::vector<int> v;

  • 即使std::move(v),因为无法修改,会退化为拷贝(const 右值无法绑定到非 const 移动构造)。


4. 与std::forward的区别

工具作用使用场景
std::move无条件转为右值明确要转移资源时
std::forward有条件转为右值(保留原始类型)模板完美转发(转发参数给下一层函数)
template<typename T> void wrapper(T&& arg) { // 保持 arg 的原始左右值属性 other_func(std::forward<T>(arg)); }

5.T&&

T&&在 C++ 中根据上下文有两种完全不同的含义,分别是右值引用万能引用(转发引用)

判断标准:T是否被推导


5.1. 右值引用(Rvalue Reference)

T具体类型时,T&&就是右值引用。

  • 作用:绑定到即将销毁的临时对象,用于实现“移动语义”,窃取资源。

  • 特征:只能接收右值(临时量、std::move的结果)。

void func(std::vector<int>&& vec) { // T 是具体类型 vector<int> // 只能接收右值 } std::vector<int> v; func(std::move(v)); // 可以 func(v); // 编译报错,左值无法绑定到右值引用

5.2. 万能引用(Forwarding Reference)

T模板参数(且T由实参推导)时,T&&是万能引用。

  • 作用:在模板中保持值类别不变(左值保持左值,右值保持右值),常用于std::forward完美转发。

  • 特征:既能接收左值,也能接收右值。

template<typename T> void func(T&& arg) { // T 由实参推导,这里是万能引用 // 传左值 -> T 推导为 int& // 传右值 -> T 推导为 int } int x = 10; func(x); // 左值可以 func(20); // 右值也可以
  • func(x)x是左值intT推导为int&arg类型是int&(左值引用)

  • func(T&& arg) → func(int& && arg) (引用折叠)→ func(int& arg)

  • func(20)20是右值intT推导为intarg类型是int&&(右值引用)

  • func(T&& arg) → func(int&& arg)

引用折叠规则

原始类型折叠后
T& &T&
T& &&T&
T&& &T&
T&& &&T&&

5.3. 特殊情况:auto&&

auto&&也是万能引用,同样根据初始化表达式推导:

auto&& r1 = 10; // 右值 -> int&& int x = 5; auto&& r2 = x; // 左值 -> int&

4. const T&&

注意:const T&&永远是右值引用,不是万能引用,且极少使用(因为移动时通常不需要 const)。

← 返回列表