C ++: Come posso evitare di “tipo di ritorno covariante non valido” in classi ereditate senza fusione?
-
18-09-2019 - |
Domanda
Ho una gerarchia di classi piuttosto complessa in cui le classi sono a croce seconda vicenda: Ci sono due classi astratte A e C contenente un metodo che restituisce un'istanza di C e A, rispettivamente. Nelle loro classi ereditate voglio usare un tipo di co-variante, che è in questo caso un problema dato che non conosco un modo per inoltrare-dichiarare la nave eredità relazione.
I ottenere una "test.cpp: 22: Errore: non valido tipo di ritorno covariante per 'virtuale D * B :: outc ()'" - l'errore in quanto il compilatore non sa che D è una sottoclasse di 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();
}
Se cambio il tipo di ritorno di B :: outc () per C * l'esempio compila. Esiste un modo per tenere B * e D * come tipi di ritorno nelle classi ereditate (sarebbe intuitivo per me che c'è un modo)?
Soluzione
Non conosco alcun modo di aver accoppiato direttamente membri covarianti in C ++. Dovrete sia per aggiungere un livello, o di implementare covariante tornare da soli.
Per la prima opzione
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();
}
e per la seconda
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();
}
Si noti che questa seconda opzione è ciò che viene fatto implicitamente dal compilatore (con alcune verifiche statiche che lo static_cast è valida).
Altri suggerimenti
Per quanto ne so, non c'è modo di farlo senza cast esplicito. Il problema è che la definizione di classe B
non può sapere che D
è una sottoclasse di C
fino a che non vede una definizione completa di classe D
, ma la definizione di classe D
non può sapere che B
è una sottoclasse di A
fino a quando non vede una definizione completa di classe B
, e in modo da avere una dipendenza circolare. Questo non può essere risolto con forward-dichiarazioni a causa di una dichiarazione anticipata, purtroppo, non può specificare una relazione di ereditarietà.
C'è un problema simile con il tentativo di implementare un metodo clone()
covariante utilizzando i modelli, che ho trovato può essere risolto , ma la soluzione analoga riesce ancora qui perché il riferimento circolare rimanga impossibile da risolvere.
Non si può fare questo a causa di lato client aspettativa. Quando si utilizza un esempio C, non si può dire che tipo di C che è (una D o qualcos'altro). Quindi, se si memorizza il puntatore B (risultante da una chiamata alla classe derivata, ma non si sa che al momento della compilazione) in una Un puntatore, non sono sicuro che tutte le cose di memoria sarà a destra.
Quando si chiama un metodo su un tipo polimorfico, l'ambiente di runtime deve controllare il tipo dinamico dell'oggetto e si muove puntatori per adattarsi al vostro gerarchia di classe. Non sono sicuro che si dovrebbe fare affidamento su di covarianza. Dai un'occhiata alla questo