移动语义在这里如何工作?

How move semantics works here?

本文关键字:工作 何工作 语义 在这里 移动      更新时间:2023-10-16

我有以下示例,我不确定我是否完全理解移动语义逻辑:

#include <iostream>
#include <string>
#include <memory>
class Player
{
public:
Player(std::string name)
: m_name(std::move(name)) {}
private:
std::string m_name;
};
int main()
{
std::string s = "Zidane";
std::cout << s << std::endl;
Player player1(s); // not moved
std::cout << s << std::endl; // s = "Zidane"
Player player2(std::move(s)); // moved -> s is empty now
std::cout << s << std::endl;
return 0;
}

我的解释是,在第一种情况下(Player1(,name实际上是在调用m_name的 ctor 之前复制的std::string类型的左值,然后,std::move作用于副本,所以最后副本是空的,其内容已使用 move ctor 移动到m_name。这就是为什么最初的论点s保持不变。正确吗?

在第二种情况下,不清楚:std::move将左值参数转换为引用右值,然后从那里会发生什么?在这种情况下,参数 s 在调用后为空。

在调用Player(std::string name)之前,始终会创建一个新std::string来"填充"name参数。

重载解析将指示是调用std::string的复制 ctor 还是移动 ctor。

案例1:Player player1(s);

新字符串由带有签名basic_string( const basic_string& other );的副本 ctor 创建,因为 's' 是一个左值。

您支付的总操作总和是字符串的 1 次移动和 1 次复制构造:

  1. 一个副本 ctor,用于将name参数复制到 ctor
  2. 单步 ctor,用于m_name类成员

案例2:Player player2(std::move(s));

新字符串由 移动 ctor 创建,用于std::string,并带有签名basic_string( basic_string&& other ) noexcept;

在第二种情况下,您调用std::move,这会将s转换为右值引用。std::string有 2 个构造函数,一个采用const std::string&,另一个采用std::string&&。右值引用可以绑定到左值引用和右值引用,但右值引用版本是更好的匹配,因此将被选中。

您支付的总操作总和是字符串的 2 个移动结构:

  1. 一招 ctor,用于name参数到 ctor
  2. 单步 ctor,用于m_name类成员

请注意,正如@aschepler和@underscore_d所指出的,std::string的移动 ctor 不需要清除源字符串。不应依赖此行为,因为它无法保证,并且取决于字符串的移动 ctor 是如何实现的。