C ++: Как я могу избежать «недопустимого ковариантного возвращаемого типа» в унаследованных классах без приведения? - PullRequest
13 голосов
/ 09 марта 2010

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

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

Ответы [ 3 ]

8 голосов
/ 09 марта 2010

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

4 голосов
/ 09 марта 2010

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

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

0 голосов
/ 09 марта 2010

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

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

...