C ++ Double Dispatch for Equals() [英] C++ Double Dispatch for Equals()

查看:104
本文介绍了C ++ Double Dispatch for Equals()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有抽象基类 形状,其派生类 Circle Rectangle

Imagine I have abstract base class Shape, with derived classes Circle and Rectangle.

class Shape {};
class Circle : public Shape {};
class Rectangle : public Shape {};

我需要确定两个形状是否相等,假设我有两个 Shape * 指针。 (这是因为我有两个实例 vector< Shape *> ,我想看看他们是否有相同的形状。)

I need to determine if two shapes are equal, assuming I have two Shape* pointers. (This is because I have two instances of vector<Shape*> and I want to see if they have the same shapes.)

这样做的推荐方法是双调度。我想出的是这里(这里大大简化,所以形状等于所有其他形状的相同类型):

The recommended way to do this is double dispatch. What I've come up with is this (greatly simplified here, so that shapes are equal to all other shapes of the same type):

class Shape {
public:
    virtual bool equals(Shape* other_shape) = 0;
protected:
    virtual bool is_equal(Circle& circle) { return false; };
    virtual bool is_equal(Rectangle& rect) { return false; };
    friend class Circle;    // so Rectangle::equals can access Circle::is_equal
    friend class Rectangle; // and vice versa
};

class Circle : public Shape {
public:
    virtual bool equals(Shape* other_shape) { return other_shape->is_equal(*this); };
protected:
    virtual bool is_equal(Circle& circle) { return true; };
};

class Rectangle : public Shape {
public:
    virtual bool equals(Shape* other_shape) { return other_shape->is_equal(*this); };
protected:
    virtual bool is_equal(Rectangle& circle) { return true; };
};

这可以工作,但我必须添加一个单独的 equals 函数和 friend 声明 Shape 然后,我必须将完全相同的 equals 函数复制 - 粘贴到每个派生类中。这是一个非常多的样板,可以说,10种不同的形状!

This works, but I have to add a separate equals function and friend declaration in Shape for each derived class. Then I have to copy-paste the exact same equals function into each derived class, too. This is an awful lot of boilerplate for say, 10 different shapes!

dynamic_cast 不是问题;太慢了。 (是的,我的基准测试,速度在我的应用程序。)

dynamic_cast is out of the question; too slow. (Yes, I benchmarked it. Speed matters in my app.)

我尝试了,但它不工作:

I tried this but it doesn't work:

class Shape {
public:
    virtual bool equals(Shape* other_shape) = 0;
private:
    virtual bool is_equal(Shape& circle) { return false; };
};

class Circle : public Shape {
public:
    virtual bool equals(Shape* other_shape) { return other_shape->is_equal(*this); };
private:
    virtual bool is_equal(Circle& circle) { return true; };
};

class Rectangle : public Shape {
public:
    virtual bool equals(Shape* other_shape) { return other_shape->is_equal(*this); };
private:
    virtual bool is_equal(Rectangle& circle) { return true; };
};

equals()总是返回false,even在相同的形状。看起来调度总是选择 is_equal(Shape&)基函数,即使更具体匹配可用。这可能是有意义的,但是我不明白C ++调度足以知道为什么。

equals() always returns false, even on identical shapes. It seems dispatch is always choosing the is_equal(Shape&) base function, even when a "more specific" match is available. This probably makes sense but I don't understand C++ dispatch well enough to know why.

推荐答案

virtual bool is_equal(Shape& circle) { return false; };

在子类中,

virtual bool is_equal(Circle& circle) { return true; };

这些是不一样的方法。你有两个单独的虚拟方法,它们都不被覆盖(它们是重载甚至没有重载,正如Ben Voigt指出的)。当调用 Shape :: is_equal 时,只有一个版本: Shape :: is_equal(Shape&)

These are not the same method. You have two separate virtual methods, neither of which is overridden (they are overloaded not even overloaded, as Ben Voigt pointed out). When you call Shape::is_equal, there is only one version: Shape::is_equal(Shape&)... which is not overridden and always returns false.

您必须在父类中定义单独的重载方法,然后在子类中覆盖它们。例如,

You would have to define the separate overloaded methods in the parent class and then override them in the child class. For example,

class Shape {
    // Choice between these two methods happens at compile time...
    virtual bool is_equal(Circle& circle) { return false; };
    virtual bool is_equal(Rectangle& circle) { return false; };
};

class Rectangle : Shape {
    // Choice between this and Shape::is_equal(Rectangle&) happens at runtime...
    virtual bool is_equal(Rectangle& circle) { return true; };
};

但是,使用这样的技巧,你可能不会接近性能或简单的C程序员会这样做:

However, using tricks like this, you will probably not approach the performance or simplicity of the way a C programmer would do it:

typedef enum {
    SHAPE_CIRCLE,
    SHAPE_RECTANGLE
} shape_type_t;

struct shape {
    shape_type_t type;
};

struct circle {
    shape_type_t type;
    ...
};

struct rectangle {
    shape_type_t type;
    ...
};

bool shape_equal(struct shape *x, struct shape *y)
{
    if (x->type != y->type)
        return false;
    switch (x->type) {
    case SHAPE_CIRCLE:
        return circle_equal((struct circle *) x, (struct circle *) y);
    case SHAPE_RECTANGLE:
        ...;
    }
}



如果重载和虚拟方法使代码更复杂比C版本,那么你可能希望重新思考你是否解决这个特殊的问题与重载和虚拟方法。

If overloading and virtual methods are making your code more complicated than the C version, then you may wish to rethink whether you solve this particular problem with overloading and virtual methods.

这篇关于C ++ Double Dispatch for Equals()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆