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

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

Я получаю "тест cpp:22: ошибка: недопустимый ковариантный возврат вводит для ‘виртуального 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*, поскольку возврат вводит в наследованных классах (это было бы интуитивно мне, что существует путь)?

13
задан Searles 9 March 2010 в 16:16
поделиться

3 ответа

Я не знаю способа напрямую связать ковариантные члены в 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).

8
ответ дан 2 December 2019 в 00:58
поделиться

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

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

4
ответ дан 2 December 2019 в 00:58
поделиться

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

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

0
ответ дан 2 December 2019 в 00:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: