如果我有一个类
class Foo{
public:
Foo(){...}
Foo(Foo && rhs){...}
operator=(Foo rhs){ swap(*this, rhs);}
void swap(Foo &rhs);
private:
Foo(const Foo&);
// snip: swap code
};
void swap(Foo& lhs, Foo& rhs);
如果我没有复制构造函数,是否有意义实现operator = by value和swap?它应该防止复制我的类Foo
的对象,但允许移动。
此类不可复制,因此我无法复制构造或复制分配它。
我用这个测试了我的代码,似乎有我想要的行为。
#include <utility>
#include <cstdlib>
using std::swap;
using std::move;
class Foo{
public: Foo():a(rand()),b(rand()) {}
Foo(Foo && rhs):a(rhs.a), b(rhs.b){rhs.a=rhs.b=-1;}
Foo& operator=(Foo rhs){swap(*this,rhs);return *this;}
friend void swap(Foo& lhs, Foo& rhs){swap(lhs.a,rhs.a);swap(lhs.b,rhs.b);}
private:
//My compiler doesn't yet implement deleted constructor
Foo(const Foo&);
private:
int a, b;
};
Foo make_foo()
{
//This is potentially much more complicated
return Foo();
}
int main(int, char*[])
{
Foo f1;
Foo f2 = make_foo(); //move-construct
f1 = make_foo(); //move-assign
f2 = move(f1);
Foo f3(move(f2));
f2 = f3; // fails, can't copy-assign, this is wanted
Foo f4(f3); // fails can't copy-construct
return 0;
}
答案 0 :(得分:5)
移动和交换确实合理。如果禁用复制构造函数,那么调用此函数的唯一方法是使用移动构造函数构造参数。这意味着如果你写
lhs = rhs; // Assume rhs is an rvalue
然后将使用移动构造函数初始化operator =
的参数的构造函数,清空rhs
并将参数设置为旧值rhs
。然后,对swap
的调用会交换lhs
的旧值和rhs
的旧值,让lhs
保留rhs
的旧值。最后,参数的析构函数触发,清理lhs
的旧内存。请注意,这实际上不是复制 - 并且交换移动 - 并且交换。
那说,你现在拥有的不正确。 std::swap
的默认实现将在内部尝试使用移动构造函数移动元素,从而产生无限递归循环。您必须重载std::swap
才能使其正常工作。
您可以在线查看here at ideone。
有关更多信息,请参阅this question及其对“四分之一规则”的讨论。
希望这有帮助!
答案 1 :(得分:2)
我认为这很好,但我真的不明白你为什么不这样做:
operator=(Foo&& rhs) // pass by rvalue reference not value
并为自己保留一个动作。
答案 2 :(得分:1)
以下是意见,我并没有真正了解0x标准,但我认为我有相当坚实的推理支持我。
没有。 事实上,根本不支持任务。
考虑语义:
“assign”表示“原因B已存在,与A相同”。 “复制”的意思是“创建B,并使其与A相同”。 “交换”是指“使B与A相同,同时使A与B相同”。 “移动”的意思是“使B与A相同,并摧毁A。”
如果我们无法复制,那么我们就无法复制和交换。复制和交换是一种实现赋值的安全方式:我们创建与A相同的C,用B交换它(因此C现在是B,B与A相同),并且销毁C (清理旧的B数据)。这根本不适用于移动和交换:我们不能在任何时候销毁A,但移动会破坏它。此外,移动不会产生新值,所以我们将A移动到B中,然后没有任何东西可以交换。
除此之外 - 使类不可复制的原因肯定不是因为“创建B”会有问题,但因为“使它与A相同”将是有问题的。 IOW,如果我们无法复制,为什么我们希望能够分配?