在可移动和不可复制的类上使用移动和交换习惯是否有意义

时间:2011-07-26 00:35:42

标签: c++ c++11 assignment-operator rvalue-reference copy-and-swap

如果我有一个类

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;
}

3 个答案:

答案 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,如果我们无法复制,为什么我们希望能够分配?