Newsgroup: it.comp.lang.c++
Titolo: Puntatori a funzioni ed errore di compilazione

Un saluto a tutti, ho il seguente problema:
ho definito una funzione che trova lo zero di una funzione con il
metodo delle secanti, in un file cpp:
double secant (double (*f)(double), double x0, double x1)
{
        [...]
}
ed il relativo header file:
double secant (double (*f)(double), double x0, double x1);
ho inoltre definito una classe che comprende tra le funzioni membro le
seguenti:
void CmyClass::calcola(void)
{
	double x,y;
	x = secant(&CmyClass::eq_prova,-2,10);
	y = eq_prova(x);
}
double CmyClass::eq_prova(double x)
{
	return (x - 4) * (x + 5);
}
Al momento della compilazione della classe mi viene mostrato
quest'errore:
c:\.....\myclass.cpp(28) : error C2664: 'secant' : cannot convert
parameter 1 from 'double (__thiscall CmyClass::* )(double)' to 'double
(__cdecl *)(double)'
potete aiutarmi? Grazie in anticipo.

 Un saluto a tutti, ho il seguente problema:
http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.1

Grazie per l'aiuto ma non riesco a risolvere il problema. La funzione
secant che ho scritto verrà richiamata più volte all'interno di varie
classi, se dichiaro la  funzione eq_prova come static il compilatore
non mi da più l'errore precedente, ma la funzione eq_prova dovrà fare
riferimento anche a membri (non-static) della classe. Qualcuno può
aiutarmi?


Nel tuo OP hai scritto:
double secant (double (*f)(double), double x0, double x1)
{
         [...]
}
il che equivale a dire che 'f' è una funzione che ha come parametro un 
double e nientaltro.
In pratica stai dicendo che 'f' non può accedere a nessuna istannza di 
classe (salvo istanze globali o create all'interno della funzione 
stessa, ovvio).
Il problema è che non è questo che poi vuoi fare in realtà. Tu vuoi 
accedre ad una classe.
In un modo o nell'altro (ce ne sono molti) la funzione secant deve 
prendere come parametro una classe.
Non ti resta che decidere fra le mille possibilità, che rientrano in una 
di queste famiglie:
A) chiami un metodo conosciuto della classe parametro (classe base 
astratta derivabile passata per reference).
B) chiami un metodo conosiuto della classe parametro (classe non 
astratta e non derivabile passata per valore che fa da trampolino e 
contiene come dati quanto serve per chiamare il codice vero).
C) come sopra, ma il metodo è sconosciuto ed è esso stesso un parametro.
oppure ridefinisci secant come template. E quindi sei agnostico ai tipi 
ma sensibile alla sintassi della loro interfaccia (e quindi 'f' può 
essere qualunque cosa che accetta una data sintassi, inclusa la chiamata 
a funzione).
Questo, unito a B di cui sopra, è il metodo "avanzato" usato dalle STL e 
da librerie tipo boost.

Mi appiglio qui per chiedere: ma in C++ si devono ancora usare (=sono 
ancora necessari) i puntatori a funzione? Non si dovrebbe evitarne l'uso 
preferendo altri sistemi (polimorfismo, overloading, passaggio di 
oggetti ecc)??


Dove puoi evitarli, evitali.
Dove non puoi ...

Ho riscritto il codice in questo modo.
template <class Xclass> double secant (double (Xclass::*F)(double),
double x0, double x1)
{
    if( x0 > x1 ) swap(x0,x1);
	double eps = 1E-10;
	int maxit = 1000;
    double x;
    int n = 0;
    do {
        double y0 = Xclass->F(x0), y1 = Xclass->F(x1);
        if (ABS(y1 - y0) < 0.001) y1 += 0.001;
        x = (x0 * y1 - x1 * y0) / (y1 - y0), x0 = x1, x1 = x;
        n++;
    } while (n < maxit && ABS(x1 - x0) >= eps);
    return x;
}
void CmyClass::calcola(void)
{
        double x,y;
        x = secant(&CmyClass::eq_prova,-2,10);
        y = eq_prova(x);
}
double CmyClass::eq_prova(double x)
{
        return (x - 4) * (x + 5);
}
myClass.obj : error LNK2019: unresolved external symbol "double __cdecl
secant<class CmyClass>(double (__thiscall
CmyClass::*)(double),double,double)"
(??$secant@VCmyClass@@@@YANP8CmyClass@@AENN@ZNN@Z) referenced in
function "public: virtual void __thiscall CmyClass::evaluate(void)"
(?evaluate@CmyClass@@UAEXXZ)
Perchè? Sto migliorando? :-) Grazie per l'aiuto.

  double y0 = Xclass->F(x0), y1 = Xclass->F(x1);
'Xclass' è una classe. La scrittura 'Xclass->F' non ha alcun significato.
Mi pare ti sfugga ancora un dettaglio essenziale: un puntatore a funzione 
membro non-static può essere utilizzato *esclusivamente* in combinazione 
un'istanza della classe.
Le sintassi possibili per invocare la funzione membro tramite F sono le 
seguenti:
 1. se disponi di un puntatore a Xclass:
     ( ptr ->* F )( x1 );
 2. se disponi di un riferimento a Xclass, o di una variabile di tipo 
Xclass:
     ( obj .* F )( x1 );
Ho messo degli spazi per mostrare che '->*' e '.*' sono degli operatori a 
sé stanti, e non combinazioni di due operatori.
Queste cose sono spiegate anche nella pagina delle FAQ che ti ho indicato 
nella mia prima risposta.
Max

 }
Generalizza f.
Se non vuoi trasformare secant in una template function, puoi provare 
questo (non ottimizzato):
 >>>
class GenFunct;
double secant(GenFunct&, double, double);
class CmyClass
{
public:
	void calcola(void);
	double eq_prova(double x);
};
class GenFunct
{
public:
	virtual ~GenFunct() {};
	double virtual operator()(double) = 0;
};
template <typename CType>
class CFunct : public GenFunct
{
	CType* C;
	double (CType::*f)(double);
public:
	CFunct(CType& c, double (CType::*fptr)(double)) : C(&c), f(fptr) {}
	CFunct(CFunct& source) : C(source.C), f(source.f) {}
	CFunct& operator=(CFunct& source) { C = source.C; f = source.f; return 
*this; }
	double operator()(double v) { return (C->*f)(v); }
};
class Funct : public GenFunct
{
	double (*f)(double);
public:
	Funct(double (*fptr)(double)) : f(fptr) {}
	Funct(Funct& source) : f(source.f) {}
	Funct& operator=(Funct& source) { f = source.f; return *this; }
	double operator()(double v) { return f(v); }
};
void CmyClass::calcola(void)
{
	double x,y;
	CFunct<CmyClass> f(*this, &CmyClass::eq_prova);
	x = secant(f,-2,10);
	y = eq_prova(x);
}
double CmyClass::eq_prova(double x)
{
	return (x - 4) * (x + 5);
}
double secant (GenFunct& f, double, double)
{
	f(0);
	return 0;
}
int main()
{
	CmyClass c;
	c.calcola();
}
<<<
Un metodo molto più semplice, ma meno definitivo, consiste nell'usare 
una variabile globale (è stato discusso in qualche post precedente).
Kiuhnm