我有以下示例,但不确定我是否完全理解移动语义逻辑:
#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个字符串的副本构造:
name
arg复制到ctor m_name
类成员情况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个移动结构:
name
arg移至ctor m_name
类成员请注意,正如@aschepler和@underscore_d所指出的那样,
std::string
的移动ctor不需要清除源字符串。人们不应该依赖于这种行为,因为不能保证这种行为,而取决于如何实现字符串的移动ctor。关于c++ - move 语义在这里如何工作?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59683835/