C++ Double Dispatch for Equals()

Представьте, что у меня есть абстрактный базовый класс Shape, с производными классами Circle и Rectangle.

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

Мне нужно определить, равны ли две фигуры, предполагая, что у меня есть два указателя Shape*. (Это потому, что у меня есть два экземпляра vector и я хочу посмотреть, есть ли у них одинаковые фигуры)

Рекомендуемый способ сделать это - double dispatch. Что я придумал, так это следующее (здесь все значительно упрощено, так что фигуры равны всем другим фигурам того же типа):

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 для каждого производного класса. Затем я должен скопировать-вставить точно такую же равно функцию в каждый производный класс тоже. Это ужасное количество шаблонов, скажем, 10 разных форм!

Есть ли более простой способ сделать это?

dynamic_cast не обсуждается; слишком медленно. (Да, я проверил его. Скорость имеет значение в моем приложении.)

Я пробовал это, но это не работает:

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, даже на одинаковых фигурах. Кажется, что диспетчерская всегда выбирает базовую функцию is_equal(Shape&), даже когда доступно "более точное" совпадение. Возможно, в этом есть смысл, но я не достаточно хорошо понимаю диспетчерскую C++, чтобы понять почему.

8
задан David Rodríguez - dribeas 12 September 2011 в 22:52
поделиться