除了swap 之外,您不应该做任何事情。基类在这方面就像一个数据成员,编译器生成的复制构造函数和赋值(如果适用,还会移动)包含它。
swap 不同,因为没有编译器生成的swap,只有命名空间std 中的默认实现,然后是程序员为用户定义的类型定义的任何特化或ADL 重载。
所以如果已经有一个看起来像这样的函数:
namespace foo {
void swap(Base &lhs, Base &rhs) {
lhs.swap(rhs); // or maybe the gory details are in this function
}
}
你什么也不做,有些用户这样称呼它:
foo::swap(derived1, derived2);
然后基础部分将被交换,而派生部分不会,这很糟糕。
用户不应该那样调用 swap,但你知道用户是什么样的。如果Base 和Derived 在同一个命名空间中,用户可能会觉得特别诱人,并且他们只是假设这一切都会奏效。因此,如果您认为您的用户(或您自己的代码库)在调用 swap 时没有原则,您可能需要确保 Derived 过载以阻止它们。
用户应致电swap 喜欢:
using std::swap;
swap(base1, base2);
swap(derived1, derived2);
当与Base 一起使用时将调用foo::swap,当与Derived 一起调用时将调用std::swap。 std::swap<Derived> 可能效率低下(如果 Base 实现了交换以提高效率,那么 Derived 也应该这样做)。因此,出于这个原因,您可能也想为Derived 实现swap,但慢并不像错误那么糟糕。
也就是说,您是在询问动作。如果Derived 可以有效地移动,那么std::swap<Derived> 也不错。也许它可以改进,也许不能,但它应该是微不足道的,因为三步很难击败。
您和用户还应该知道swap 不能很好地处理多态性(因为通常两个对象必须具有相同的完整类型才能使它们交换才有意义),并且为基类实现它可能惹麻烦。
Derived d1, d2;
Base &b1 = &d1, &b2 = d2;
swap(b1, d2); // swaps just the base part
swap(b1, b2); // swaps just the base part
首先,用户需要知道不要那样调用swap。其次,您可能需要考虑 Base 和 swap 是否真的被精心设计为基类。