C++:Как я могу избежать “недопустимого ковариантного возвращаемого типа” в унаследованных классах без приведения?

StackOverflow https://stackoverflow.com/questions/2410532

  •  18-09-2019
  •  | 
  •  

Вопрос

У меня есть довольно сложная иерархия классов, в которой классы перекрестно зависят друг от друга:Существуют два абстрактных класса A и C, содержащих метод, который возвращает экземпляр C и A соответственно.В их унаследованных классах я хочу использовать тип co-variant, что в данном случае является проблемой, поскольку я не знаю способа переадресации отношения наследования ship.

Я получаю "test.cpp:22:ошибка:недопустимый ковариантный возвращаемый тип для ‘virtual D * B::outC()’" -ошибка, поскольку компилятор не знает, что D является подклассом C.

class C;

class A {
public:
        virtual C* outC() = 0;
};

class C {
public:
        virtual A* outA() = 0;
};


class D;

class B : public A {
public:
        D* outC();
};

class D : public C {
public:
        B* outA();
};

D* B::outC() {
        return new D();
}

B* D::outA() {
        return new B();
}

Если я изменю возвращаемый тип B::outC() на C *, пример будет скомпилирован.Есть ли какой-нибудь способ сохранить B * и D * в качестве возвращаемых типов в унаследованных классах (мне было бы интуитивно понятно, что такой способ есть)?

Это было полезно?

Решение

Я не знаю ни одного способа иметь непосредственно связанные ковариантные члены в C ++.Вам придется либо добавить слой, либо самостоятельно реализовать ковариантный возврат.

Для первого варианта

class C;

class A {
public:
        virtual C* outC() = 0;
};

class C {
public:
        virtual A* outA() = 0;
};


class BI : public A {
public:
};

class D : public C {
public:
        BI* outA();
};

class B: public BI {
public:
        D* outC();
};

D* B::outC() {
        return new D();
}

BI* D::outA() {
        return new B();
}

и для второго

class C;

class A {
public:
        C* outC() { return do_outC(); }
        virtual C* do_outC() = 0;
};

class C {
public:
        virtual A* outA() = 0;
};


class D;

class B : public A {
public:
        D* outC();
        virtual C* do_outC();
};

class D : public C {
public:
        B* outA();
};

D* B::outC() {
        return static_cast<D*>(do_outC());
}

C* B::do_outC() {
        return new D();
}

B* D::outA() {
        return new B();
}

Обратите внимание, что этот второй вариант выполняется компилятором неявно (с некоторыми статическими проверками допустимости static_cast).

Другие советы

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

Аналогичная проблема возникает при попытке реализовать ковариантный clone() метод с использованием шаблонов, который, как я обнаружил, может быть решен, но аналогичное решение здесь по-прежнему терпит неудачу, потому что циклическую ссылку по-прежнему невозможно разрешить.

Вы не можете этого сделать из-за ожиданий на стороне клиента.При использовании экземпляра C вы не можете определить, какой это тип C (D или что-то еще).Таким образом, если вы сохраните указатель B (полученный в результате вызова производного класса, но вы не знаете его во время компиляции) в указатель A, я не уверен, что все данные о памяти будут правильными.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top