std::move

来自cppreference.com
< cpp‎ | utility
 
 
工具库
语言支持
类型支持(基本类型、RTTI)
库功能特性测试宏 (C++20)
动态内存管理
程序工具
协程支持 (C++20)
变参数函数
调试支持
(C++26)
三路比较
(C++20)
(C++20)(C++20)(C++20)
(C++20)(C++20)(C++20)
通用工具
日期和时间
函数对象
格式化库 (C++20)
(C++11)
关系运算符 (C++20 中弃用)
整数比较函数
(C++20)(C++20)(C++20)   
(C++20)
交换类型运算
(C++14)
(C++11)
(C++11)
move
(C++11)
(C++17)
常用词汇类型
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++17)
(C++23)
初等字符串转换
(C++17)
(C++17)

 
在标头 <utility> 定义
template< class T >
typename std::remove_reference<T>::type&& move( T&& t ) noexcept;
(C++11 起)
(C++14 前)
template< class T >
constexpr std::remove_reference_t<T>&& move( T&& t ) noexcept;
(C++14 起)

std::move 用于指示对象 t 可以“被移动”,即允许从 t 到另一对象的高效资源传递。

具体而言,std::move 生成标识其实参 t亡值表达式。它严格等价于到右值引用类型的 static_cast

参数

t - 要被移动的对象

返回值

static_cast<typename std::remove_reference<T>::type&&>(t)

注解

右值实参(如临时对象的纯右值或如 std::move 所产生的亡值之一)调用函数时,重载决议选择接受右值引用形参的版本(包含移动构造函数移动赋值运算符及常规成员函数,如 std::vector::push_back)。若实参标识一个占有资源的对象,则这些重载拥有移动实参所保有的任何资源的选择,但不强求如此。例如,链表的移动构造函数可以复制指向表头的指针,并将 nullptr 存储到实参中,而非分配并复制逐个结点。

右值引用变量的名称是左值,而若要绑定到接受右值引用形参的重载,就必须转换到亡值,此乃移动构造函数移动赋值运算符典型地使用 std::move 的原因:

// 简单的移动构造函数
A(A&& arg) : member(std::move(arg.member)) // 表达式 "arg.member" 为左值
{} 
 
// 简单的移动赋值运算符
A& operator=(A&& other)
{
    member = std::move(other.member);
    return *this;
}

一个例外是当函数形参类型是转发引用(看起来像到模板形参的右值引用)时,该情况下应转而使用 std::forward

除非另外指定,否则所有已被移动的标准库对象都被置于“合法但未指定的状态”,这意味着该对象的类不变式成立(因此只有无前条件的函数,例如赋值运算符,才能安全地在对象被移动后使用):

std::vector<std::string> v;
std::string str = "example";
v.push_back(std::move(str)); // str 现在合法但未指定
str.back(); // 若 size() == 0 则为未定义行为:back() 拥有前条件 !empty()
if (!str.empty())
    str.back(); // OK,empty() 没有前条件而 back() 的前条件已满足
 
str.clear(); // OK,clear() 无前条件

而且,以亡值实参调用的标准库函数可以假设该实参是到对象的唯一引用;若它以 std::move 从左值构造,则不进行别名检查。然而标准库类型的自移动赋值保证将对象置于合法(但未指定的)状态:

std::vector<int> v = {2, 3, 3};
v = std::move(v); // v 的值未指定

示例

#include <iomanip>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
 
int main()
{
    std::string str = "Salut";
    std::vector<std::string> v;
 
    // 使用 push_back(const T&) 重载,
    // 表示我们将带来复制 str 的成本
    v.push_back(str);
    std::cout << "复制后,str 为 \"" << str << "\"\n";
 
    // 使用右值引用 push_back(T&&) 重载,
    // 表示不复制字符串;而是
    // str 的内容被移动进 vector。
    // 这个开销比较低,但也意味着 str 现在可能为空。
    v.push_back(std::move(str));
    std::cout << "移动后,str 为 " << std::quoted(str) << '\n';
 
    std::cout << "vector 的内容是 {" << std::quoted(v[0])
              << ", " << std::quoted(v[1]) << "}\n";
}

可能的输出:

复制后,str 为 "Salut"
移动后,str 为 ""
vector 的内容是 {"Salut", "Salut"}

参阅

(C++11)
转发一个函数实参
(函数模板)
若移动构造函数不抛出则获得右值引用
(函数模板)
(C++11)
将某一范围的元素移动到一个新的位置
(函数模板)