Newsgroup: it.comp.lang.c++
Titolo: virtual function template

salve a tutti, ho un problema.
quello che avrei bisogno è un meccanismo di polimorfismo applicato ad una funzione della quale non conosco il tipo che restituisce. faccio un esempio
class base
{
	public:
	virtual void ret() const = 0;
}
class der1 : public base
{
	public:
	int ret() const {...}
}
class der2 : public base
{
	public:
	float ret() const {...}
}
quello che avrei bisogno di fare è una cosa tipo:
	base* p = new der1;
	base* k = new der2;
	p->ret() // restituisce un int
	k->ret() // restituisce un float
il problema è che una cosa del genere non è permessa, ovvero cercare di ridefinire una funzione virtuale cambiando il tipo di ritorno. Ho provato con una funzione virtuale template ma non è permesso specificare funzioni virtuali template, come
template <typename T>
virtual T ret() const = 0;
quindi io non so proprio come risolvere il problema, e l'utilizzo di un discriminante non mi piace...
come posso fare? grazie! 
Muzero

 
il tipo di ritorno non è sufficiente per differenziare due diverse versioni 
di uno stesso metodo.
Il paradigma ad oggetti consente di poter esprimere il polimorfismo tramite 
overloading dei metodi però cambiando solo i parametri formali e non il 
tipo di ritorno, quindi IMHO credo che tu ti debba "appoggiare" a qualche 
mezzuccio tecnico che ti offre il linguaggio di turno (in questo caso il 
C++) per poter ottenere quello che vuoi.
P.S. In questi casi domandati: "ma se il paradigma non mi consente di fare 
una certa cosa, starò progettando correttamente il mio sistema?" Nel tuo 
caso (tiro a caso), ha senso ottenere due diversi valori di ritorno dal 
nome di una stessa funzione senza nemmeno passargli un parametro? (può 
esserlo benissimo per carità, però è un modo per cercare di capire se si 
sta facendo la cosa giusta)
ciao!
__________________________________
"Ama e fai c'ho che vuoi".... 
__________________________________

         k->ret() // restituisce un float
Riflettiamo: a che ti può servire una cosa del genere? Comunque il
valore restituito, essendo diverso, lo devi trattare in modo diverso. Ad
esempio assegnandolo a variabili di tipo diverso
int i = p -> ret();
float f = k -> ret();
e allora, forse, meglio dare nomi differenti ai metodi
int i = p -> retInt();
float f = k -> retFloat();
Oppure convertendone il valore
float f1 = p -> ret();
float f2 = k -> ret();
ma allora perché non fare la conversione a monte definendo il metodo
ret() come restituente un float?
Se ci sono altri motivi che ora mi sfuggono allora dicceli.
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg

Si in effetti non ho detto a cosa mi serve una cosa del genere:
sto scrivendo un interprete e siccome il linguaggio non e' tipizzato mi servirebbe poter creare uno stack di variabili non conoscendone il tipo ovvero volevo fare una cosa cosi':
ho 10 variabili (5 intere, 2 real e 3 stringhe) e creo uno stack di 10 puntatori (un doppio puntatore) base 
nel quale alloco le mie 3 classi derivate da base, esempio:
tipobase *stack[0] = new tipoInt;
tipobase *stack[1] = new tipoInt;
tipobase *stack[2] = new tipoReal;
tipobase *stack[3] = new tipoString;
mi avrebbe fatto piacere che il valore di ritorno fosse restituito da una funzione come ret() e implicito dal tipo di oggetto che vado a considerare ... l'unica cosa che posso fare e' utilizzare un discriminante...
ma io volevo usare il polimorfismo, uffa! ^_^
Muzero

 considerare ...
Ma anche se fosse, poi cosa ci fai con quel valore? Non devi forse
appoggiarlo dentro qualche variabile di tipo ben definito o farci
operazioni ben precise? Vedrai che se rispondi a questa domanda ti
accorgi che quello che chiedi non ha senso (a meno che non abbia capito
male io, ovviamente) e comunque il tipo lo devi gestire manualmente.


 tipobase *stack[3] = new tipoString;
beh, in questo caso puoi (anzi, devi!) fare cosi`:
class Expression {
public:
     virtual tipobase* eval(context) = 0;
};
class IntExpr: public Expression {
public:
     tipoInt* eval(context) { ... }
};
class StringExpr: public Expression {
public:
     tipoString* eval(context) { ... }
};
In pratica in una classe D derivata da B puoi cambiare il tipo di ritorno 
di una funzione virtuale, basta che il tipo della funzione in D sia 
derivato dal tipo della funzione in B: forse e` un po' complicato da 
leggere, pero` dall'esempio dovresti capirlo; questa caratteristica si 
chiama covarianza.
ciao Anto
-- 
"Computer science is not about computers any more than astronomy
is about telescopes." -- EW Dijkstra

 ciao Anto
bello da morire!!! thx a lot! 
Muzero

 ma io volevo usare il polimorfismo, uffa! ^_^
Oltre alla covarianza di cui ti ha parlato Antonio Cuni, ti serviranno
un po' di operatori di conversione in modo da trasformare le tue classi
fra di loro quando possibile: immagino che dovrebbe essere possibile
p.e. sommare un int con un double o una stringa convertibile in numero
con altro numero... Eccoti uno schema di approccio:
class Variant
{
public:
 typedef enum {
  VT_Int,
  VT_Double,
  VT_String
 } VType;
 Variant(VType vt, int v) : m_eType(vt), m_iValue(v) {};
 Variant(VType vt, double v) : m_eType(vt), m_dValue(v) {};
 Variant(VType vt, const std::string& v) : m_eType(vt), m_sValue(v) {};
 virtual ~Variant() = 0;
 virtual operator int() const {return m_iValue;};
 virtual operator double() const {return m_dValue;};
 virtual operator std::string() const {return m_sValue;};
 virtual Variant& operator=(const Variant& rhs);
protected:
 VType m_eType;
 int m_iValue;
 double m_dValue;
 std::string m_sValue;
};
class IVariant : public Variant
{
public:
 IVariant(int v = 0) : Variant(VT_Int, v) {};
 IVariant(const IVariant& rhs) : Variant(VT_Int, rhs.m_iValue) {};
 virtual ~IVariant() {};
 virtual operator int() const {return m_iValue;};
 virtual operator double() const {return
static_cast<double>(m_iValue);};
 virtual operator std::string() const {return
num2string<int>(m_iValue);};
 virtual IVariant& operator=(const Variant& rhs);
 virtual IVariant operator+(const Variant& rhs);
};
Variant::~Variant() {}
class DVariant : public Variant
{
public:
 DVariant(double v = 0.0) : Variant(VT_Double, v) {};
 DVariant(const DVariant& rhs) : Variant(VT_Double, rhs.m_dValue) {};
 virtual ~DVariant() {};
 virtual operator int() const {return static_cast<int>(m_dValue);};
 virtual operator double() const {return m_dValue;};
 virtual operator std::string() const {return
num2string<double>(m_dValue);};
 virtual DVariant& operator=(const Variant& rhs);
 virtual DVariant operator+(const Variant& rhs);
};
class SVariant : public Variant
{
public:
 SVariant(const std::string& v = "") : Variant(VT_String, v) {};
 SVariant(const SVariant& rhs) : Variant(VT_String, rhs.m_sValue) {};
 virtual ~SVariant() {};
 virtual operator int() const {return toInt();};
 virtual operator double() const {return toDouble();};
 virtual operator std::string() const {return m_sValue;};
 virtual SVariant& operator=(const Variant& rhs);
 virtual SVariant operator+(const Variant& rhs);
};
Variant& Variant::operator=(const Variant& rhs)
{
 switch (m_eType) {
  case Variant::VT_Int: m_iValue = static_cast<int>(rhs); return *this;
  case Variant::VT_Double: m_dValue = static_cast<double>(rhs); return
*this;
  case Variant::VT_String: m_sValue = static_cast<std::string>(rhs);
return *this;
 }
 throw std::runtime_error("invalid type");
 return *this;
}
IVariant& IVariant::operator=(const Variant& rhs)
{
 m_iValue = static_cast<int>(rhs);
 return *this;
}
IVariant IVariant::operator+(const Variant& rhs)
{
 return IVariant(m_iValue + static_cast<int>(rhs));
}
DVariant& DVariant::operator=(const Variant& rhs)
{
 m_dValue = static_cast<double>(rhs);
 return *this;
}
DVariant DVariant::operator+(const Variant& rhs)
{
 return DVariant(m_dValue + static_cast<double>(rhs));
}
SVariant& SVariant::operator=(const Variant& rhs)
{
 m_sValue = static_cast<std::string>(rhs);
 return *this;
}
SVariant SVariant::operator+(const Variant& rhs)
{
 return SVariant(m_sValue + static_cast<std::string>(rhs));
}
Questo e' solo uno scheletro incompleto (in molti casi ci sono
ambiguita' se non si definiscono gli operatori), ma dovrebbe rendere
l'idea. In particolare in questo modo puoi fare cose come:
 IVariant v1(5), v1_T;
 DVariant v2(3.14), v2_T;
 SVariant v3("10"), v3_T;
 v1_T = v2 + v1; //v1_T contiene 8
 v2_T = v2 + v1; //v2_T contiene 8.14
 v3_T = v2 + v1; //v3_T contiene "8.14"
 v1_T = v1 + v2; //v1_T contiene 8
 v2_T = v1 + v2; //v2_T contiene 8.0
 v3_T = v1 + v2; //v3_T contiene "8"
 v1_T = v3 + v2; //v1_T contiene 103
 v2_T = v3 + v2; //v2_T contiene 103.14
 v3_T = v3 + v2; //v3_T contiene "103.14"
 v1_T = v2 + v3; //v1_T contiene 13
 v2_T = v2 + v3; //v2_T contiene 13.14
 v3_T = v2 + v3; //v3_T contiene "13.14"
Se la stringa non e' convertibile in numero dovrebbe essere generata
un'eccezione.
Ti ricordo che la covarianza e' una caratteristica relativamente recente
e non tutti i compilatori la supportano (fra questi, il Visual C++ 6).
Ciao
Gian Michele

 sta facendo la cosa giusta)
Oppure: non e' che il paradigma e' troppo limitato, e mi pone attorno
delle pastoie artificiali?
In effetti a seconda dei casi ci sono almeno due soluzioni per il
problema dell'OP:
Senza eredita':
template <typename T> class der {
  public:
    T ret();
};
Con eredita':
template <typename T> class base {
  public:
    virtual T ret() = 0;
};
class der1 : public base<int> {
  public:
    int ret();
};
class der2 : public base<float> {
  public:
    float ret();
};
Ciao,
Nicola

 chiama covarianza.
Gia'. Peccato che poi non tutti i compilatori la accettino.
L'avevo utilizzata una volta per una libreria grafica, utilizzando il
mio fido gcc.
Al momento di compilarla con VC 6.0 (perche' il cliente, purtroppo,
vuole quello) ho dovuto rimodificarla tutta e restituire sempre il tipo
base, affidandomi poi a dei cast dal lato utilizzatore. :(
 bye
	av.