Разрешение переопределения C ++ с помощью миксинов и множественного наследования - PullRequest
0 голосов
/ 29 ноября 2018

Я озадачен поведением ниже, что я не могу объяснить.Я пытался написать содержательное название, но я не уверен, что прибил это.

Сначала немного фона.Я пишу программное обеспечение для численного моделирования и пытаюсь немного модернизировать свой код.У меня есть три числовых решателя: primal, dual и adaptive.primal решатель делает свою работу, ничего не зная;решатель dual имеет много общего с primal, но никогда не используется отдельно, а решатель adaptive - это оболочка, которая содержит и запускает primal и dual в этом порядке.Дело в том, что когда решатель adaptive запускает свой решатель primal, primal необходимо выполнить некоторые дополнительные операции по сравнению с тем, когда он используется один.Пусть одна такая операция будет save.Кроме того, primal и dual имеют много общего, поэтому я решил, что они должны совместно использовать код в классе common.

Это был мой первоначальный дизайн

#include <iostream>

using namespace std;

template <typename s>
struct common
{
    void run()
    {
        static_cast<s*>(this)->save();
    }
};

struct primal : public common<primal>
{
    virtual void save()
    {
        cout << "  Primal\n";
    }
};

struct dual : public common<dual>
{
    void save()
    {
        cout << "  Dual\n";
    }
};

struct adaptive : public primal,
                  public dual
{
    void run()
    {
        primal::run();
        dual::run();
    }

    virtual void save()
    {
        primal::save();
        cout << "    Adaptive\n";
    }
};

int main(int argc, const char *argv[])
{
    adaptive a;
    a.run();
    return 0;
}

, и этопечатает

  Primal
    Adaptive
  Dual

, что для меня означает, что

  1. a.run вызывает primal::run = common<primal>::run
  2. common<primal>::run вызывает primal::save, но этот методэто virtual, поэтому он разрешает вызов adaptive::save
  3. adaptive::save вызовов primal::save явно, потому что я хочу, чтобы он выполнял обычные задачи primal плюс что-то еще
  4. adaptive::save выполняет какую-то другую работу и возвращает
  5. a.run, вызывает dual::run = common<dual>::run, что вызывает dual::save, но этот метод не virtual, поэтому он выбран без учета adaptive::save.

Пока все хорошо, и это именно то, что я хотел.Итак, первый вопрос: верно ли все вышесказанное?

Теперь вот и проблемы.После некоторого рефакторинга primal::save и dual::save теперь являются одной и той же функцией, поэтому я хочу поместить ее в common и продолжать расширять поведение только в adaptive.

В качестве первого шага яизменил код на

template <typename s>
struct common
{
    void run()
    {
        save();
    }

    virtual void save()
    {
        cout << "This should never be called\n";
    }
};

// Everything else stays the same

и теперь вывод равен

  Primal
    Adaptive
  Primal
    Adaptive

, в то время как я ожидал того же вывода, что и раньше.Это первое, что я не понимаю: мне кажется, что теперь вызов dual::save, сделанный dual::run(), разрешается вплоть до adaptive::save, в то время как я ожидал, что он остановится на dual::save, так какэтот метод не virtual.Это мой второй вопрос: почему это происходит?Чем отличается от ранее?

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

#include <iostream>

using namespace std;

template <typename s>
struct common
{
    void run()
    {
        save();
    }

    virtual void save()
    {
        cout << s::name << "::common\n";
    }
};

struct primal : public common<primal>
{
    static const string name;
};
const string primal::name = "primal";

struct dual : public common<dual>
{
    static const string name;
};
const string dual::name = "dual";

struct adaptive : public primal,
                  public dual
{
    virtual void save()
    {
        primal::save();
        cout << "    Adaptive\n";
    }

    void run()
    {
        primal::run();
        dual::run();
    }
};

int main(int argc, const char *argv[])
{
    adaptive a;
    a.run();

    return 0;
}

ион печатает

primal::common
    Adaptive
primal::common
    Adaptive

как предыдущая версия, тогда как я ожидал

primal::common
    Adaptive
dual::common

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

Спасибо за то, что прочитали все до конца, любая подсказка приветствуется!Я должен придерживаться C ++ 98, если это возможно.

1 Ответ

0 голосов
/ 29 ноября 2018

Сначала объяснение, почему вы получаете вывод, который видите.

a.run звонки adaptive.run.adaptive.run сначала явно вызывает primal::run, который вызывает виртуальную функцию save, adaptive::save.

adaptive::save явно вызывает primal::save (common<primal>::save) и выводит "primal :: common».Затем мы возвращаемся обратно в adaptive::save и выводим «Adaptive», затем возвращаемся обратно к adaptive::run.

Теперь мы явно вызываем dual::run (common<dual>::run).Это вызовет виртуальную функцию save, которая снова будет иметь значение adaptive::save, дублирующее ваш существующий вывод.

Как исправить это, чтобы получить то, что вы хотите?

Это зависит от некоторых деталей, которых нет в вашем вопросе.Вот некоторые возможности.

Простейшим способом было бы явным образом вызвать dual::save из adaptive::save, но я предполагаю, что это не сработает для вас, так как dual нужно выполнить, прежде чем это может произойти.

Вы можете adaptive::save ничего не делать, а затем вызвать save для обоих подобъектов в run.

Вы можете сделать run virtual и переопределить в dual класс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...