Виртуальные функции друзей? - PullRequest
0 голосов
/ 06 октября 2018

Я хочу создать интерфейс, подобный

class Scalar {
public:
  Scalar() {}
  virtual ~Scalar() {}
  //virtual members operators
  virtual Scalar& operator+() const = 0;
  virtual const Scalar operator-() const;
  virtual Scalar& operator=() = 0;
  virtual Scalar& operator+=() = 0;
  //...
};

Я намереваюсь также использовать некоторые дружественные функции, например:

    friend const Scalar operator+(const Scalar&, const Scalar&);

Но есть проблема, когда я получаю абстрактный класси создайте производный класс, скажем:

class RealNumber: public Scalar {
public:
  friend const RealNumber operator+(const RealNumber&, const RealNumber&);
  //some definitions...
};

В соответствии с этой логикой мне нужно будет определить новую перегрузку друга operator+ для каждого нового класса, производного от Scalar.Есть ли способ решить эту проблему и избежать объявления этих друзей во всех производных классах?

Ответы [ 2 ]

0 голосов
/ 06 октября 2018

Возможно, вам не удастся создать виртуальные функции друзей, но вы можете создавать виртуальных операторов (даже operator + может быть сделано таким образом).

Рассмотрите следующий код: ПРЕДУПРЕЖДЕНИЕ: ЭТО НЕ ХОРОШИЙ ДИЗАЙН ВСЕХ

#include <iostream>
using namespace std;

class AA {
private:
    int a;
public:
    AA(int a): a(a) {};
    inline int getA() const { return a; };
    virtual AA operator +(const AA &a) {
        AA res(this->getA() + a.getA());
        return res;
    }
};

class BB: public AA {
    public:
    BB(int a): AA(a) {}
    virtual AA operator +(const AA &a) {
        AA res(this->getA() - a.getA());
        return res;
    }
};

int main() {
    BB tmp(1);
    AA& a = tmp;
    AA b(7);
    cout << (a + b).getA();
    return 0;
}

Когда я писал этот код, я обнаружил, что может быть вызвано множество недостатков (например, оператор +, который вместо этого действительно вычитаетКроме того, что, если вторым операндом был BB вместо первого ??)

Итак, по поводу вашей проблемы, вам нужны скаляры.Таким образом, вы можете сделать следующий подход:

#include <iostream>
using namespace std;

// Here, Scalar acts as an abstract class, all its goal is to convert your
// class type into some calculable value type (e.g. you can use T as double)
template <typename T>
class Scalar {
public:
    // Converter function is abstract
    virtual operator T() = 0;
};

class AA: public Scalar<double> {
private:
    double a;
public:
    inline double getA() {return a;};
    AA(double a): a(a) {}
    // Implements the converter function in Scalar, T in scalar is mapped
    //    to double because we did Scalar<double>
    virtual operator double() {
        return a;
    }
};

class BB: public Scalar<double> {
private:
    int a;
public:
    inline double getA() {return (double)a;};
    BB(int a): a(a) {}
    virtual operator double() {
        return (double)a;
    }
};

int main() {
    BB tmp(1);
    AA b(7);
    // Here, it is easy for us just to add those, they are automatically converted into doubles, each one like how it is programmed.
    cout << (b + tmp);
    return 0;
}
0 голосов
/ 06 октября 2018

Это ваша проблема?

Я понимаю, что ваша проблема в том, что ваши двое друзей ссылаются на совершенно разные функции, поскольку у них разные подписи:

friend const Scalar operator+(const Scalar&, const Scalar&);
friend const RealNumber operator+(const RealNumber&, const RealNumber&);

Хуже, выборвнешнего друга класса не будет полиморфным: правильный друг будет выбран на основе типа времени компиляции.

Как ее решить?

Прежде всего, вместо использования внешнего перегруженного друга, вы можете рассмотреть возможность переопределения оператора самого класса (сохранение идентичной подписи).

Однако здесь есть две основные проблемы:

  • почти невозможно вернуть ссылку из арифметического оператора, если вы не примете побочные эффекты, которые затем заставят вашего оператора вести себяиначе, чем ожидалось.
  • вам нужно справиться с объединением различных скаляров: что если вам нужно добавить Scalar к RealNumber?Это потребует реализации двойной отправки , чтобы справиться со всеми возможными комбинациями.

Итак, тупик?

Нет, есть две другие альтернативы, в зависимости от вашей реальной проблемы:

  • Вы действительно хотите динамически комбинировать арифметический тип во время выполнения?Если да, вам нужно отойти от подхода переопределения операторов C ++ и реализовать оценщик выражений, используя шаблон интерпретатора .
  • Если нет, рассмотрите возможность использования дизайна на основе шаблонов, чтобы компилятор выбирал во время компиляции соответствующую специализацию.
  • Или страдайте от множества возможных друзей и их комбинации типов параметров.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...