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

#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的ctor之前复制了std::string(它是m_name类型的左值),然后std::move作用于副本,因此最终副本为空,并且副本为空。内容已使用move ctor移至m_name。这就是为什么原始参数s保持不变的原因。这是正确的吗?

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

最佳答案

在调用std::string之前,总是会创建一个新的name来“填充” Player(std::string name)参数。

重载分辨率将决定是否调用std::string的复制ctor或移动ctor。

情况1:Player player1(s);
新字符串由副本ctor使用签名basic_string( const basic_string& other );创建,因为“s”是左值。

您支付的总操作费用为1个 Action 和1个字符串的副本构造:

  • 一个副本ctor,将name arg复制到ctor
  • m_name类成员
  • 的一动ctor

    情况2:Player player2(std::move(s));
    新字符串由move ctor为std::string创建,带有签名basic_string( basic_string&& other ) noexcept;
    在第二种情况下,调用std::move,它将s转换为右值引用。 std::string有2个构造函数,一个构造函数采用const std::string&,另一个构造函数采用std::string&&。一个右值ref可以绑定(bind)到一个左值ref和一个右值ref,但是右值ref版本是更好的匹配,因此将其选中。

    您支付的操作总和是一个字符串的2个移动结构:
  • 一动ctor,将name arg移至ctor
  • m_name类成员
  • 的一动ctor

    请注意,正如@aschepler和@underscore_d所指出的那样,std::string的移动ctor不需要清除源字符串。人们不应该依赖于这种行为,因为不能保证这种行为,而取决于如何实现字符串的移动ctor。

    关于c++ - move 语义在这里如何工作?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59683835/

    10-13 09:29