Newsgroup: it.comp.lang.c++
Titolo: Template specializzati

Compilatore MIcrosoft Visual C++ 6.0
Ho dei problemi con la specializzazione dei template. 
Ecco il codice che mi mette in crisi:
template < typename A_> A_ clone(A_ a){return a;};
template < int > int clone(int a){return 0;};
int main(int argc, char* argv[])
{
	int	b = 2;
	char	c = 2;
	float e = 2.0;
	b = clone<int>(1);
	b = clone(b);
	c = clone(1);
	e = clone(1.0);
	return 0;
}
A b non viene mai assegnato zero. 
Dove sta l'inghippo?
--
Tieni per te il Papero se vuoi la mia email
Sito Papero -> 
http://utenti.tripod.it/papero

 template < int > int clone(int a){return 0;};
Credo che la forma corretta per la specializzazione di una funzione
template in questo caso sia una delle seguenti:
template <> int clone<int>(int a){return 0;};
   template <> int clone<>(int a){return 0;};
     template <> int clone(int a){return 0;};
Con il gcc, tutte e tre le forme funzionano e ho il comportamento che ci
si aspetta (b==0). Con la forma da te usate, una delle versioni del gcc
che uso esce con un "internal compiler error", mentre quella più recente
che ho a portata di mano (la 2.95.2) si comporta come il tuo VC++6.
Max


template in questo caso sia una delle seguenti:
Incidentalmente ho scoperto che funzionava:
template int clone<int>(int a){return 0;};
Non so cosa dice pero' lo standard, poiche' MSDN su questo aspetto
tace, (oppure ho cercato male)
Sospetto che la sintassi corretta sia:
template <> int clone<int>(int a){return 0;};
++++
Ho visto anche che se
template <typename A_>
class Papero
{
public
	A_ Nulla(A_ a);
};
devo poi scrivere 
template <typename A_>
inline A_ Papero<typename A_>::Nulla(A_ a)
{
	return a;
}
Il fatto che mi sembra mi sembra ridondante il che torni a specificare
il tipo di template in "Papero<typename A_>" quando questocontenuto
informativo e' presente nella riga precedente. Non capito insomma
perche' non e' lecita la sintassi:
template <typename A_>
inline A_ Papero::Nulla(A_ a)
{
	return a;


 }
Questa sintassi ha un significato diverso. 
Si tratta della definizione del membro Nulla<T> della classe Papero (non
la definizione del membro Nulla della classe Papero<T> ). Andrebbe bene,
se avessimo una classe Papero di questo tipo:
 class Papero
 { 
	template< typename T >
	inline T Nulla( T a );
 }
Max

 }
Questo tipo di Papero pero' come si deriva?
Se voglio la mia anitra, come devo scriverla?
class Anitra: public Papero?
Se poi voglio il polimorfismo sulla funzione nulla nella mia classe
Anitra? Sinceramente non capisco perche' l'unica sintassi ammissibile
non sia:
template <typename T>
class Papero
{
public:
	T Nulla(T a);
};
Insomma, continuo a non capire.
--
Tieni per te il Papero se vuoi la mia email
E' arrivata la Quack Template Library per il VB!


 
-- 
"Time flies like an arrow; fruit flies like a banana."

Davide,
credo che Max non stesse cercando di mostrarti un modo diverso di fare
le cose, ma solo perche' la sintassi da te proposta. In soldoni se hai:
template <class A> class Class1 {
  A f(A a);
}
devi scrivere
template <class A> A Class1<A>::f(A a) { return a; }
Se hai:
class Class2 {
  template <class A> A f(A a);
}
devi scrivere
template <class A> A Class2::f(A a) { return a; }
Se hai
template <class B> class Class3 {
  template <class A> A f(A a);
}
devi scrivere
template <class B> template <class A> A Class3<B>::f(A a) { return a; }
Insomma il tuo e' solo un problema di sintassi. Spero di averti chiarito
le idee.
Ciao,
Nicola

sintassi diverse per esprimere cose completamente diverse. 
A me sembrano due sintassi diverse per esprimere la stessa cosa!
Il discorso della funzione virtuale poi' non vedo cosa centri.
Se io ho
class Anitra
{
 public:
   int Quack(void);
};
posso naturalmente scrivere
class SuperAnitra: public Anitra
{
public:
  int Quack(int numero);
};
senza evere tirato in campo i template.
Passiamo oltre. Con 
template <typeclass T_>
TAnitra 
{
public:
    T_ Quack(void);
};
io esprimo (secondo me) la stessa cosa che con
TAnitra 
{
public:
   template <typeclass T_>
    T_ Quack(void);
};
con un dettaglio, riesco a derivare la prima con
class SuperAnitra: public Anitra<int>
{
public:
  int Quack(int numero);
};
mentre per la seconda non capisco quale sarebbe la sintassi.
Esiste?
++++
La seconda considerazione e' perche'
TAnitra 
{
public:
   template <typeclass T_>
    T_ Quack(void);
}
dovrebbe essere concettualmente diversa da
template <typeclass T_>
TAnitra 
{
public:
    T_ Quack(void);
};
Per me sono identiche come concetto


le cose, 
Non contesto certo Max per l'aiuto che mi sta dando per chiarirmi le
idee. Non capisco come potrei fare certe cose con quella sintassi
(derivazione di un' "istanza" particolare di un template).
Non capisco nemmeno perche' lui le considera cose radicalmente
diverse. Dov'e' la diversita', in soldoni?


 Per me sono identiche come concetto
Per il C++, invece (come per la matematica e per
il buon senso) una classe di cui ciascun oggetto
ha un'infinita famiglia di metodi, e una famiglia di
classi di cui ciascun oggetto ha un metodo solo,
ben lungi dall'essere concetti identici, sono del
tutto distinti.
Alex

 
Uhmm... Ma non sei stato tu a tirare in ballo il polimorfismo?
Quello che cercavo di dire è che mentre la seguente dichiarazione è
ammissibile:
 template < class T >
 class Papero {
	virtual T pippo( T t );	
 };
quest'altra non lo è:
 class Papero {
	template < class T > 
        virtual T pippo( T t ); 
 };
Max

base.
Perche' nasconde? Una a un parametro, e l'altra nessuno.
Tutte e due rimangono chiaramente visibili.  


 Tutte e due rimangono chiaramente visibili.  
Non esattamente. Considera queste classi:
 class A {
  public:
  void pippo() {};
 };
 class B: public A {
  public:
  int pippo(int i) { return 1; }
 }
Come potrai constatare, il seguente codice non si lascia compilare:
  B b;
  b.pippo();
Questo perchè l'overload non ha luogo con funzioni dichiarate in "class
scope" differenti. Per ottenere l'effetto voluto si può ricorrere a una
using-declaration della dichiarazione di B:
 using A::pippo
(Ma non provate a farlo con il gcc. Non funziona. Bug documentato.)
Max

 Tutte e due rimangono chiaramente visibili.
No, non e` questa la regola del linguaggio C++ (ancora
una volta, questo non c'entra con i template: si applica a
*TUTTE* le classi definite nel linguaggio).  La VISIBILITA`
di un nome in uno "scope" lessicale dipende *solo dal
nome stesso*, NON dagli specifici usi del nome (come,
ad esempio, i diversi overload di una funzione); una
*qualsiasi* dichiarazione in uno "scope lessicale annidato"
NASCONDE *tutte* le dichiarazioni in scope lessicali
piu` esterni; le classi (derivata/base) si comportano da
questo punto di vista ESATTAMENTE come gli altri scope
(interno/esterno).
Esempio banale:
struct Palmipede {
    void Quack();
};
struct Palmipedone: public Palmipede {
    void Quack(int);
};
void provaunpoquesta(Palmipedone* p)
{
    p->Quack(23);    // ok
    p->Quack();        // ***ERRORE***
    Palmipede* pp = p;
    pp->Quack();      // ok
}
Se il tuo compilatore non ti da` errore alla linea
marcata col commento ERRORE, allora non stai
usando un compilatore C++ conformante.  (Non
conosco nessun compilatore che presenti questo
specifico, e grave, bug).
L'incomprensione di questa particolarita` e` una
causa abbastanza frequente di errori da parte del
programmatore: una di migliaia e migliaia cause
del genere, dovute all'*estrema* complessita` del
linguaggio C++ (apposta consiglio SEMPRE di usare
altri linguaggi piu` semplici, al posto del C++, per
qualsiasi compito che appena appena lo consenta!)
e all'insufficiente preparazione dei programmatori.
Alex

piu` esterni; 
Ero convinto del contrario. Dopo anni di VB e' evidente che ho
dimenticato completamente quel poco che sapevo del C++.
Prometto che quando ho finito di studiare i DB torno al C++ :-(
Comunque bastava:
void provaunpoquesta(Palmipedone* p)
{
    p->Quack(23);    // ok
    p->Palmipede::Quack();   
}
Non sopporto i cast. Anzi, se ho capito la storia dei template, non
capito perche' questa sintassi e' possibile. Il nascondere e'
parziale.
su 
void pippo(void)
{
	int i;
	{
		int i;
		//con il cavolo riesco ad accedere alla variabile piu' esterna 
	}
}
++++
Ma se vogliamo continuare con la mia sorpresa, e' stato lo scoprire
che questo codice e' legale
class Palmipede 
{
public:
	void Quack(void){};
};
class PalmipeTimido: public Palmipede 
{
private:
	void Quack(void){};
};
void provaunpoquesta(PalmipeTimido* p)
{
    p->Palmipede::Quack();  
}
int main(int argc, char* argv[])
{
	PalmipeTimido* a = new PalmipeTimido;
	provaunpoquesta(a);
	delete a;
	return 0;
}
Se eredito una classe non posso fare niente per nascondere le funzioni
ereditate. Cio' non mi piace per niente. La classe base potrei non
averla fatta io, e ritenerla "non giusta".


 averla fatta io, e ritenerla "non giusta".
In questo caso, dovrai NON ereditare (pubblicamente).
Quando erediti pubblicamente da X, asserisci al mondo "io sono un X,
usabile intercambiabilmente dovunque X sia usabile" (principio di
sostituibilita` di Liskov).
Se cosi` non e`, NON ereditare (pubblicamente).
Alex

In questo caso, dovrai NON ereditare (pubblicamente).
Hai perfettamente ragione.
Il fatto che ho sempre digerito male la possibilita' di ereditare in
modo private, protected, e pubblic, oltre se virtual o non virtual.
Per lungo tempo mi sono chiesto cosa era cosa giusta da fare, poi mi
sono messo ad ereditare in modo pubblico sempre.
Sono curioso di vedere di vedere come implementeranno l'eredita' 
in VB 7, anche se dalle indiscrezioni pare assomigliera' piu a Java
che non al C++


 sono messo ad ereditare in modo pubblico sempre.
Meglio non ereditare mai [l'implementazione], se non sai quel che stai
facendo; ereditando, se mai, la sola interfaccia (se e solo se ti
interessa impegnarti a realizzarla, ovviamente -- di solito per fare
usare polimorficamente la tua classe) e realizzandola per contenimento
e delegazione (o meglio, con la PWAN di Coplien, naturalmente).
Alex