移动语义在这里如何工作?
How move semantics works here?
我有以下示例,但我不确定我是否完全理解移动语义逻辑:
#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
是类型 std::string
的左值,实际上是在 m_name
的构造函数之前被复制的调用,然后 std::move
作用于副本,所以最后副本为空,其内容已使用 move ctor 移动到 m_name
。这就是为什么原始参数 s
保持不变的原因。正确吗?
在第二种情况下,不清楚:std::move
将左值参数转换为引用右值,然后发生了什么?在这种情况下,调用后参数 s 为空。
在调用 Player(std::string name)
之前,总是会为 'populate' name
参数创建一个新的 std::string
。
重载决议将决定是否调用 std::string
的复制构造函数或移动构造函数。
案例 1:Player player1(s);
新字符串由具有签名 basic_string( const basic_string& other );
的复制构造函数创建,因为 's' 是一个左值。
您支付的操作总和为 1 次移动和 1 次字符串复制构造:
- 一份构造函数,用于构造函数的
name
arg
-
m_name
class 成员 一步操作者
案例二:Player player2(std::move(s));
新字符串由 std::string
的移动构造函数创建,签名为 basic_string( basic_string&& other ) noexcept;
在第二种情况下,您调用 std::move
,它将 s
转换为右值引用。 std::string
有 2 个构造函数,一个接受 const std::string&
,另一个接受 std::string&&
。一个右值引用可以绑定到一个左值引用和一个右值引用,但是右值引用版本是一个更好的匹配,所以它会被选中。
您支付的操作总和为字符串的 2 次移动构造:
- 一步构造函数,
name
arg 到构造函数
-
m_name
class 成员 一步操作者
请注意,正如@aschepler 和@underscore_d 所指出的,std::string
的移动构造函数不需要清除源字符串。人们不应该依赖于这种行为,因为它不能保证并且取决于字符串的移动构造函数是如何实现的。
我有以下示例,但我不确定我是否完全理解移动语义逻辑:
#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
是类型 std::string
的左值,实际上是在 m_name
的构造函数之前被复制的调用,然后 std::move
作用于副本,所以最后副本为空,其内容已使用 move ctor 移动到 m_name
。这就是为什么原始参数 s
保持不变的原因。正确吗?
在第二种情况下,不清楚:std::move
将左值参数转换为引用右值,然后发生了什么?在这种情况下,调用后参数 s 为空。
在调用 Player(std::string name)
之前,总是会为 'populate' name
参数创建一个新的 std::string
。
重载决议将决定是否调用 std::string
的复制构造函数或移动构造函数。
案例 1:Player player1(s);
新字符串由具有签名 basic_string( const basic_string& other );
的复制构造函数创建,因为 's' 是一个左值。
您支付的操作总和为 1 次移动和 1 次字符串复制构造:
- 一份构造函数,用于构造函数的
name
arg -
m_name
class 成员 一步操作者
案例二:Player player2(std::move(s));
新字符串由 std::string
的移动构造函数创建,签名为 basic_string( basic_string&& other ) noexcept;
在第二种情况下,您调用 std::move
,它将 s
转换为右值引用。 std::string
有 2 个构造函数,一个接受 const std::string&
,另一个接受 std::string&&
。一个右值引用可以绑定到一个左值引用和一个右值引用,但是右值引用版本是一个更好的匹配,所以它会被选中。
您支付的操作总和为字符串的 2 次移动构造:
- 一步构造函数,
name
arg 到构造函数 -
m_name
class 成员 一步操作者
请注意,正如@aschepler 和@underscore_d 所指出的,std::string
的移动构造函数不需要清除源字符串。人们不应该依赖于这种行为,因为它不能保证并且取决于字符串的移动构造函数是如何实现的。