7

如果我有一堂课,比如

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= 并交换是否有意义?它应该防止复制我的类对象,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;
}
4

3 回答 3

6

移动和交换确实是合理的。如果禁用复制构造函数,则调用此函数的唯一方法是使用移动构造函数构造参数。这意味着如果你写

lhs = rhs; // Assume rhs is an rvalue

然后参数的构造函数operator =将使用移动构造函数初始化,清空rhs并将参数设置为的旧值rhs。调用swapthen 交换lhs' 旧值和rhs' 旧值,留下lhs持有rhs' 旧值。最后,参数的析构函数触发,清理lhs旧的内存。请注意,这实际上不是复制和交换,而是移动和交换。

也就是说,你现在所拥有的是不正确的。的默认实现std::swap将在内部尝试使用移动构造函数来移动元素,这会导致无限递归循环。您必须超载std::swap才能使其正常工作。

您可以在 ideone在线查看此内容。

有关详细信息,请参阅此问题及其对“四个半规则”的讨论。

希望这可以帮助!

于 2011-07-26T00:52:12.737 回答
2

我认为这很好,但我真的不明白你为什么不这样做:

operator=(Foo&& rhs) // pass by rvalue reference not value

并为自己节省一步。

于 2011-07-26T02:21:56.200 回答
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,如果我们不能复制,我们为什么要期望能够分配?

于 2011-07-26T02:40:00.570 回答