Как иметь два чисто виртуальных метода с одинаковым именем, но разными типами возвращаемых данных - PullRequest
0 голосов
/ 30 мая 2018

У меня есть интерфейс A, который является общедоступным, и внутренний класс реализации C, например, так:

struct A
{
    virtual void f() = 0;
};

struct C : public A
{
    virtual void f() override
    {
    }
};

Я хотел бы постепенно перейти на новый интерфейсB.B будет иметь те же методы, однако типы возвращаемых данных могут отличаться.Я хотел бы, чтобы мой класс реализации реализовал как A, так и B, чтобы унаследованный код мог продолжать использовать A, пока он не будет перенесен, но новый код может быть написан с помощью B.Я знаю, что если у меня есть два чисто виртуальных метода с абсолютно одинаковой сигнатурой, мне нужно только одно переопределение в моем классе реализации.Но если они различаются по типу возвращаемого значения, я не знаю, как это сделать ...

struct A
{
    virtual void f() = 0;
};

struct B
{
    virtual int f() = 0;
};


struct C : public A, public B
{
    // How do I implement both versions of f() here??
};

Ответы [ 3 ]

0 голосов
/ 31 мая 2018

Я бы сделал это так:

struct A {}
struct B {}
struct OldImpl : public A {}
struct NewImpl : public B {}
struct Combined : public AImpl, public BImpl {}

Затем вы можете передать объединение во все, что хочет A или B. Проблема в том, что ваши OldImpl и NewImpl полностью разделены.Им не разрешают взаимодействовать друг с другом;Это означает, что вы не можете вызвать setFoo() в A и получить то же значение в getFoo() в B. Что, вероятно, не то, что вы хотите из того, что вы описали.

Чтобы решить эту проблему, не создавайте2-й интерфейс.В любом случае это не сработает, потому что там, где xxx() может быть недействительным сейчас, а int позже, вы, вероятно, захотите что-то сделать с возвращаемым значением.Если вы действительно хотите сделать это в пошаговом режиме, создайте ветвь, измените возвращаемое значение одной функции, протестируйте его, объедините, повторите.

0 голосов
/ 31 мая 2018

Спасибо за хорошие отзывы!Но я нашел свое собственное решение: старые добрые необязательные параметры!

struct A
{
    virtual void f() = 0;
};

struct B
{
    virtual int f(int ignoreMe = 0) = 0;
};


struct C : public A, public B
{
    virtual void f() override {}
    virtual int f(int) override {}
};

Никакой двусмысленности для внешних пользователей, использующих A или B, и никаких параметров менять не нужно.Внутренняя неопределенность также отсутствует, поскольку параметр не является необязательным в классе реализации.Необязательный параметр можно удалить вместе с A после завершения миграции.

0 голосов
/ 30 мая 2018

Это по крайней мере компилируется с VS2017:

struct A
{
    virtual void f() = 0;
};

struct A2: public A
{
    void f() { Af(); }
    virtual void Af() = 0;
};

struct B
{
    virtual int f() = 0;
};

struct B2 : public B
{
    int f() { return Bf(); }
    virtual int Bf() = 0;
};

struct C : public A2, public B2
{
    void Af() {}
    int Bf() { return 42; }
};

Редактировать: Не удалось добавить этот комментарий к собственному ответу Пола Акцизано, поэтому я поместил его здесь.

Слегка изменив ответ Пола.Сделав неиспользуемый параметр по умолчанию защищенным классом, вы по-прежнему будете правильно проверять тип и избегать возможности конфликта типов с другой функцией-членом.

struct A
{
    virtual void f() = 0;
};

struct B
{
protected:
    class disambiguator {};
public:
    virtual int f(disambiguator ignoreMe = disambiguator()) = 0;
};


struct C : public A, public B
{
    virtual void f() override {}
    virtual int f(disambiguator ignoreMe) override { return 42; }
};

A *createA() { return new C; }
B *createB() { return new C; }

Одним из приложений для этого может быть постепенный переход от rawуказатели на умные указатели.

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