移动构造函数是否需要可移动的属性?
Do move constructors need attribute that are moveable?
我正在努力研究移动构造函数,并希望通过这个问题获得更多见解。这是一个简单的 class.
class A
{
private:
vector<B> Bs;
public:
/*
..
*/
A(A&& other)
: Bs
{
Bs = other.Bs;
}
}
即使 B
没有移动构造函数,我的移动构造函数看起来是否正确?即使我没有为classB
的对象显式编写移动赋值,移动构造函数是否有效?如果不是,是否意味着如果要移动任何对象,首先必须确保每个属性也是可移动的?
如果您的对象包含可移动的对象(例如 std::vector
),默认移动构造函数将负责移动,因此您无需执行任何操作。尝试使用 Rule of Zero.
在你的情况下,不,move ctor 不会做正确的事情。它会复制,因为 in
Bs = other.Bs;
other.Bs
是函数内部的左值,因为它有一个名称(是的,它指的是右值引用,但 other
本身是一个左值)。你需要
Bs = std::move(other.Bs);
或更好
A(A&& other) : Bs(std::move(other.Bs)) {}
但是,在这种情况下,您真的根本不应该编写任何用户定义的移动构造函数。
强烈推荐阅读 Howard Hinnant, the person who contributed probably the most to the concept of move semantics: http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014
不,不是。您的线路:
Bs = other.Bs;
您正在进行复制作业。在移动构造函数体中不会改变表达式值类型。左值仍然是左值,右值仍然是右值。
要进行移动分配,它看起来像这样:
Bs = std::move(other.Bs);
但它仍然没有达到应有的效率。
还有一个问题。您的代码未编译,因为您已经为 std::vector
构造函数提交了大括号。事实上,这是您应该移动值的位置。这是一个例子:
// a kitten dies when your move constructor is not noexcept
A(A&& other) noexcept
// in the move constructor of A, we move construct it's member too.
: Bs{std::move(other.Bs)}
// empty body
{}
最好的解决方案是:</code></p>
<p>你是对的,它是空的。如果您不放置任何构造函数,编译器会为您完成。</p>
<p>如果您想添加其他构造函数但让编译器添加它自己的移动构造函数,您可以显式默认它:</p>
<pre><code>// bonus: noexcept when it can.
A(A&&) = default;
最后一件事。如果你的 class 包含另一个不可移动但可复制的,它将尽其所能:复制构造它。
让类型 C
成为不可移动的 class 但可复制的。
这是一个例子:
// the `C&&` from the move will bind to the `const C&` of the operator=
auto anotherC = std::move(aC);
此处的移动构造函数将是 "effective",因为它将在 A
object 从类型 A
的右值构造时调用。它会工作得很好,创建另一个 object 的有效副本。但是,它并没有达到预期的效果,因为它复制了内部 std::vector
object。这是一个性能问题,而不是正确性问题。
这里实际上有两个性能问题。首先是构造函数默认构造了B
subobject,然后赋值给它。那太浪费了。要解决这个问题:
A::A(A&& other) : B(other.B) {
}
第二个是移动构造函数可以从另一个构造函数中窃取object。为此,实施应该从 B
subobject:
A::A(A&& other) : B(std::move(other.B)) {
}
最后,为了回答标题中的问题,std::move
可以很好地处理不可移动的类型。它所做的只是将左值转换为右值; non-movable 类型(通常)可以从右值复制,所以除非你处理的是非常反常的类型,否则只需对 object 中的几乎所有内容使用 std::move
。 (移动一个 int 值可能看起来很奇怪,但它是无害的)。
我正在努力研究移动构造函数,并希望通过这个问题获得更多见解。这是一个简单的 class.
class A
{
private:
vector<B> Bs;
public:
/*
..
*/
A(A&& other)
: Bs
{
Bs = other.Bs;
}
}
即使 B
没有移动构造函数,我的移动构造函数看起来是否正确?即使我没有为classB
的对象显式编写移动赋值,移动构造函数是否有效?如果不是,是否意味着如果要移动任何对象,首先必须确保每个属性也是可移动的?
如果您的对象包含可移动的对象(例如 std::vector
),默认移动构造函数将负责移动,因此您无需执行任何操作。尝试使用 Rule of Zero.
在你的情况下,不,move ctor 不会做正确的事情。它会复制,因为 in
Bs = other.Bs;
other.Bs
是函数内部的左值,因为它有一个名称(是的,它指的是右值引用,但 other
本身是一个左值)。你需要
Bs = std::move(other.Bs);
或更好
A(A&& other) : Bs(std::move(other.Bs)) {}
但是,在这种情况下,您真的根本不应该编写任何用户定义的移动构造函数。
强烈推荐阅读 Howard Hinnant, the person who contributed probably the most to the concept of move semantics: http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014
不,不是。您的线路:
Bs = other.Bs;
您正在进行复制作业。在移动构造函数体中不会改变表达式值类型。左值仍然是左值,右值仍然是右值。
要进行移动分配,它看起来像这样:
Bs = std::move(other.Bs);
但它仍然没有达到应有的效率。
还有一个问题。您的代码未编译,因为您已经为 std::vector
构造函数提交了大括号。事实上,这是您应该移动值的位置。这是一个例子:
// a kitten dies when your move constructor is not noexcept
A(A&& other) noexcept
// in the move constructor of A, we move construct it's member too.
: Bs{std::move(other.Bs)}
// empty body
{}
最好的解决方案是:</code></p>
<p>你是对的,它是空的。如果您不放置任何构造函数,编译器会为您完成。</p>
<p>如果您想添加其他构造函数但让编译器添加它自己的移动构造函数,您可以显式默认它:</p>
<pre><code>// bonus: noexcept when it can.
A(A&&) = default;
最后一件事。如果你的 class 包含另一个不可移动但可复制的,它将尽其所能:复制构造它。
让类型 C
成为不可移动的 class 但可复制的。
这是一个例子:
// the `C&&` from the move will bind to the `const C&` of the operator=
auto anotherC = std::move(aC);
此处的移动构造函数将是 "effective",因为它将在 A
object 从类型 A
的右值构造时调用。它会工作得很好,创建另一个 object 的有效副本。但是,它并没有达到预期的效果,因为它复制了内部 std::vector
object。这是一个性能问题,而不是正确性问题。
这里实际上有两个性能问题。首先是构造函数默认构造了B
subobject,然后赋值给它。那太浪费了。要解决这个问题:
A::A(A&& other) : B(other.B) {
}
第二个是移动构造函数可以从另一个构造函数中窃取object。为此,实施应该从 B
subobject:
A::A(A&& other) : B(std::move(other.B)) {
}
最后,为了回答标题中的问题,std::move
可以很好地处理不可移动的类型。它所做的只是将左值转换为右值; non-movable 类型(通常)可以从右值复制,所以除非你处理的是非常反常的类型,否则只需对 object 中的几乎所有内容使用 std::move
。 (移动一个 int 值可能看起来很奇怪,但它是无害的)。