3

我想向两个现有的 C++ 类添加交换功能。一个类继承自另一个类。我希望每个类的实例只能与同一类的实例交换。为了使其半具体化,假设我有 Foo 和 Bar 类。Bar 继承自 Foo。我定义了 Foo::swap(Foo&) 和 Bar::swap(Bar&)。Bar::swap 代表 Foo::swap。我希望 Foo::swap 仅适用于 Foo 实例,而 Bar::swap 仅适用于 Bar 实例:我不知道如何强制执行此要求。

这是给我带来麻烦的示例:

#include <algorithm>
#include <iostream>

struct Foo {
    int x;
    Foo(int x) : x(x) {};

    virtual void swap(Foo &other) {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        std::swap(this->x, other.x);
    };
};

struct Bar : public Foo {
    int y;
    Bar(int x, int y) : Foo(x), y(y) {};

    virtual void swap(Bar &other) {
        std::cout << __PRETTY_FUNCTION__ << " ";
        Foo::swap(other);
        std::swap(this->y, other.y);
    };
};

void display(Foo &f1, Foo &f2, Bar &b34, Bar &b56)
{
    using namespace std;

    cout << "f1:  " << f1.x                  << endl;
    cout << "f2:  " << f2.x                  << endl;
    cout << "b34: " << b34.x << " " << b34.y << endl;
    cout << "b56: " << b56.x << " " << b56.y << endl;
}

int main(int argc, char **argv)
{
    {
        Foo f1(1), f2(2);
        Bar b34(3,4), b56(5,6);
        std::cout << std::endl << "Initial values: " << std::endl;
        display(f1,f2,b34,b56);
    }

    {
        Foo f1(1), f2(2);
        Bar b34(3,4), b56(5,6);
        std::cout << std::endl << "After Homogeneous Swap: " << std::endl;
        f1.swap(f2);             // Desired
        b34.swap(b56);           // Desired
        display(f1,f2,b34,b56);
    }

    {
        Foo f1(1), f2(2);
        Bar b34(3,4), b56(5,6);
        std::cout << std::endl << "After Heterogeneous Member Swap: " << std::endl;
        // b56.swap(f2);         // Doesn't compile, excellent
        f1.swap(b34);            // Want this to not compile, but unsure how
        display(f1,f2,b34,b56);
    }

    return 0;
}

这是输出:

Initial values: 
f1:  1
f2:  2
b34: 3 4
b56: 5 6

After Homogeneous Swap: 
virtual void Foo::swap(Foo&)
virtual void Bar::swap(Bar&) virtual void Foo::swap(Foo&)
f1:  2
f2:  1
b34: 5 6
b56: 3 4

After Heterogeneous Member Swap: 
virtual void Foo::swap(Foo&)
f1:  3
f2:  2
b34: 1 4
b56: 5 6

您可以在最终输出组中看到 f1.swap(b34) 以一种可能令人讨厌的方式“切片” b34。我希望有罪的行在运行时不编译或炸毁。由于涉及到继承,如果我使用非成员或朋友交换实现,我想我会遇到同样的问题。

如果有帮助,该代码可在键盘上找到。

出现此用例是因为我想将交换添加到 boost::multi_array 和 boost::multi_array_ref。multi_array 继承自 multi_array_ref。只有将 multi_arrays 与 multi_arrays 交换,将 multi_array_refs 与 multi_array_refs 交换才有意义。

4

5 回答 5

5

交换,例如赋值和比较,适用于值类型,不适用于类层次结构的基础。

我一直发现最容易遵循 Scott Meyer 的有效 C++ 建议,即不从具体类派生而只使叶类具体化。然后,您可以安全地将 swap、operator== 等实现为仅用于叶节点的非虚拟函数。

虽然可以拥有虚拟交换功能,但拥有虚拟基类的全部意义在于在运行时具有动态行为,所以我认为你是一个失败者,试图让所有不正确的可能性在编译时失败。

如果你想走虚拟交换路线,那么一种可能的方法是做这样的事情。

class Base
{
public:
    virtual void Swap(Base& other) = 0;
};

class ConcreteDerived
{
    virtual void Swap(Base& other)
    {
        // might throw bad_cast, in this case desirable
        ConcreteDerived& cother = dynamic_cast<ConcreteDerived&>(other);bad_cast
        PrivateSwap(cother);
    }

    void PrivateSwap(ConcreteDerived& other)
    {
        // swap implementation
    }
};
于 2009-11-30T22:36:30.377 回答
3

(有点hacky的解决方案)

添加一个受保护的虚方法isBaseFoo(),使其在Foo中返回true,在Bar中返回false,Foo的swap方法可以检查它的参数是否有isBaseFoo()==true。

邪恶,并且仅在运行时检测到问题,但我想不出更好的方法,尽管查尔斯贝利的答案可能会更好,如果你允许 dynamic_cast<>。

于 2009-11-30T23:02:45.740 回答
2

你不能真正做到这一点,但无论如何我不明白这一点。它并不比切片operator=或复制构造函数更糟糕,而且你也无法避免后者。为什么应该swap有所不同?

出于同样的原因,虚拟化可能不值得swap,同样的原因你不operator=虚拟化。

于 2009-11-30T22:35:20.117 回答
1

我认为这种情况现在可以通过 C++11 中移动语义的存在来解决。我通常仅使用交换函数来避免分配中的复制重复,因此它仅由需要扩展交换函数并且知道其基的静态类型的派生类静态使用,因此不需要虚拟性(如据说会导致切片的细微问题)。事实上,我在我的基础中将交换函数声明为受保护的方法,以确保它不会直接在其他任何地方使用。如有必要,最具体的类可以公开最终版本。

于 2015-09-27T16:38:56.497 回答
0

您实际上要做的是从第三方继承层次结构中交换类的实例。鉴于此,我会忘记在实际类上使用交换并添加一个间接级别。使用 boost::shared_ptr 是一个好方法;使用包含您想要的任何类的 shared_ptr 实例,并交换到您心中的内容。

一般来说,由于其他回答者描述的所有原因,在编译时解决问题是很困难的。

于 2009-11-30T23:22:06.167 回答