C ++: Come posso evitare di “tipo di ritorno covariante non valido” in classi ereditate senza fusione?

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

  •  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)?

È stato utile?

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top