giovedì, maggio 31, 2007

C++: problemi con il polimorfismo

Questo post sarebbe da mettere come domanda in un newsgroup di programmazione in C++.
Non so se a qualcuno di voi è mai capitato, però a me è la seconda volta che succede di definire un metodo virtuale puro in una superclasse, convinto di poterlo implementare nella sottoclasse (simpatico e banale, anche se potente, design pattern "Abstract method").. e di vedere un bel messaggio di errore del linker che non trova l'implementazoine del metodo se si tenta di chiamarlo!

VOLETE IL CODICE? OK!

Allora di una cosa sono sicuro: con l'operatore di assegnamento ottengo il seguente risultato:

------------------------------------------------------------------------------------

#include
using namespace std;

// The abstract superclass:
class A {
public:
// The virtual pure methods:
virtual A& operator +=(A& a)=0;
virtual A& operator =(A& a)=0;
};

// My subclass
class B : public A {
public:
// The implementations:
virtual A& operator +=(A& a) {
cout << "Called the '+=' operator!" << endl;
return *this;
}
virtual A& operator =(A& a) {
cout << "Called the '=' operator!" << endl;
return *this;
}
};

// Using them:
int main(int argc, char *argv[]) {
// Instantiating:
B b1,b2;
b1 += b2; // This works:
b1 = b2; // This give a compile time error:
return 0;
}

------------------------------------------------------------------------------------

il che potrebbe essere QUASI accettabile.. se non fosse che non c'è modo di richiedere forzatamente l'implementazione nelle sottoclassi dell'operatore di assegnamento. Provate a sostituire i parametri dal tipo A& al tipo B&.. e vedete come s'incazza il compilatore!!!
Il codice che l'altra volta mi aveva dato lo stesso errore era su un metodo normalissimo.. fra l'altro con un altro compilatore (nemmeno da dire cambi compilatore perchè è un baco.. il baco è NEL MIO CERVELLO!).

Ok.. se trovate un modo per richiedere forzatamente (stile signature nell'interfaccia) l'implementazione dell'assegnamento nelle sottoclassi.. ditemelo perché la soluzione banale NON FUNZIONA :(

2 commenti:

Gabriele Lana ha detto...

Ciao bell'uomo, non so che cosa stai tentando di fare, ma ti posso dire perchè non funziona

"b1 = b2" è equivalente a "b1.operator=(b2)" che può essere risolto dal compilatore solo chiamando "B& B::operator=(B&)" infatti chiamando "A& B::operator=(A&)" staresti assegnando un riferimento ad un oggetto di tipo A ad uno di tipo B, il che è sbagliato

Dovendo chiamare "B& B::operator=(B&)" (metodo che viene generato automaticamente dal compilatore stesso), il compilatore si accorge che non sa come copiare A in quanto il metodo in questione è virtuale e non è stato implementato

Potresti utilizzare un parametro di ritorno covariante ed avere "B& B::operator=(A&)" che sovrascrive il metodo virtuale "A& A::operator=(A&)", ma purtroppo il c++ è dotato di single dispatching, ovvero il metodo chiamato viene scelto in base al run-time type dell'oggetto che riceve il messaggio (in questo caso b1) e non anche in base ai parametri (ovvero l'overloading viene risolto a compile time), quindi il compilatore chiamerebbe sempre e comunque "B& B::operator=(B&)" che soffre dello stesso problema di prima

Per far felice il compilatore dovresti implementare "B& B::operator=(B&)" e "A& B::operator=(A&)" (in alternativa "A& A::operator=(A&)")

Nota: è vivamente sconsigliato rendere virtuale l'operatore di assegnamento, ti consiglio di leggerti qualche thread a riguardo su comp.lang.c++

Gabriele Lombardi ha detto...

Grazie, preciso come sempre. Avevo intuito che fosse quello il problema, ma non trovando soluzioni accettabili alla fine ho raggirato l'ostacolo in maniera abbastanza elegante realizzando un setter che, fra le altre cose, nel mio particolare caso probabilmetne è anche più comodo e adeguato agli scopi.
Questo post l'ho fatto perchè non è la prima volta che mi capita di incappare in questo problema, mi è capitato anche in VC++ in un normalissimo metodo virtuale puro implementato in una sottoclasse.. quella volta fra l'altro gli argomenti erano invarianti.. se riesco a recuperare il backup giusto te lo passo volentieri (nota: in VC++ mi dava l'errore mentre gcc non faceva una piega)

Grazie Uomo.. alla prox!