PDA

Visualizza la versione completa : [C++] Utilizzo dei template che mi crea errori


Neptune
19-11-2010, 15:13
Salve a tutti,
avevo un programma in C++ che implementava delle liste con cursore con determinati oggetti, inizialmente il tipo di oggetti inseribile era solo di tipo "int", mentre la posizione degli oggetti anch'essa di tipo "int".

Ora ho cercato di templatizzare il tutto in modo che gli oggetti possano diventare di qualsiasi tipo, ed appartenendo la lista con cursori ad una superclasse che dovrò ampliare di liste di vario genere volevo templetizzare anche la posizione (che su altre diventerà un indirizzo e non un indice intero).

Il punto è che è la prima volta che provo ad utilizzare i template ed il compilatore, nel mio caso il dev, mi riempie di errori di vario tipo e non riesco a capire se ho commesso "errori sintattici", "errori logici" o un pò ed un pò.

Praticamente mi da sempre questi due errori ripetuti per le intestazioni di ogni metodo nel file .cpp (ovvero sul " template < class T, class P>" e sull'intestazione del metodo):



42 C:\Users\Neptune\Desktop\Asd\laboratorio\10-11-2010 V.2\Lista con cursori\listap.cpp expected constructor, destructor, or type conversion before "classe_spazio"
42 C:\Users\Neptune\Desktop\Asd\laboratorio\10-11-2010 V.2\Lista con cursori\listap.cpp expected `;' before "classe_spazio"


Inoltre mi dice sia nel file .cpp che nel .h


94 C:\Users\Neptune\Desktop\Asd\laboratorio\10-11-2010 V.2\Lista con cursori\listap.h `classe_spazio' does not name a type
10 C:\Users\Neptune\Desktop\Asd\laboratorio\10-11-2010 V.2\Lista con cursori\listap.cpp `classe_spazio' does not name a type


Ovvero non mi riconosce la classe spazio? perchè?

Vi incollo il codice sperando possiate darmi una mano:

listap.h


#ifndef _LISTAP_H
#define _LISTAP_H
#define N 100 //dimensione del vettore
#include <iostream>
#include <string>

using std::string;
using namespace std;

// SUPERCLASSE:

template< class T, class P >
class Superclasse{


public:
typedef T tipoelem;
typedef P posizione;

virtual void crealista()=0;
virtual bool listavuota() const =0;
virtual posizione primoLista() const =0 ;
virtual posizione succlista(posizione) const =0;
virtual posizione preclista(posizione) const =0;
virtual bool finelista(posizione) const =0;
virtual tipoelem leggilista(posizione) const =0;
virtual void scrivilista(tipoelem, posizione)=0;
virtual void inslista(tipoelem,posizione)=0;
virtual void canclista(posizione p)=0;

bool palindroma();
int lunghezza();
void inverti();

};


template< class T, class P >
class classe_spazio
{
public:
typedef T tipoelem;
typedef P posizione;
struct TcomponenteSpazio
{
tipoelem elemento;
posizione successivo;
posizione precedente;
TcomponenteSpazio()
{
elemento=0;
successivo=precedente=-1;
}
};

classe_spazio();
posizione getTesta();
posizione getCoda();
void setCoda(posizione);
void setTesta(posizione);
inline TcomponenteSpazio & operator[](size_t Index)
{
return spazio[Index];
}

private:
TcomponenteSpazio spazio[N];
posizione componenteSpazio_testa;
posizione componenteSpazio_coda;
};

template< class T, class P >
class Lista : public Superclasse<T, P> {

public:
typedef T tipoelem;
typedef P posizione;

Lista();


void crealista();
bool listavuota() const;
posizione primoLista() const;
posizione succlista(posizione) const;
posizione preclista(posizione) const;
bool finelista(posizione) const;
tipoelem leggilista(posizione) const;
void scrivilista(tipoelem, posizione);
void inslista(tipoelem,posizione);
void canclista(posizione p);

private:
static classe_spazio spazio;
int testa;
int coda;

};

//-----------------------------------------------------------------------------------------------------------

#endif // _LISTAP_H

Neptune
19-11-2010, 15:13
Qui invece implemento le funzioni (troppo lungo non mi entrava tutto in un unico thread):

listap.ccp


* Liste: Rappresentazione collegata circolare (con sentinella)
* realizzata mediante doppi puntatori (o simmetrica)
*/

#include "listap.h"
/************************************************** ****************************/
/*Metodi classe_spazio*/
/************************************************** ****************************/
template< class T, class P >
classe_spazio Lista<T,P>::spazio;

template< class T, class P >
classe_spazio<T,P>::classe_spazio()
{
int i;
//inizializzo l'elemento 0 e lo collego al primo
componenteSpazio_testa = 0;
spazio[0].elemento = 0;
spazio[0].precedente = -1;
spazio[0].successivo = 1;
//inizializzo gli elementi da 1 a N-1 collegandoli tra loro
for(i=1; i < N-1; i++)
{
spazio[i].elemento = 0;
spazio[i].precedente = i-1;
spazio[i].successivo = i+1;
}
//inizializzo l'N-esimo elemento
componenteSpazio_coda = i;
spazio[i].elemento = 0;
spazio[i].precedente = i-1;
spazio[i].successivo = -1;
}

template< class T, class P >
posizione classe_spazio<T,P>::getTesta()
{
return componenteSpazio_testa;
}

template< class T, class P >
posizione classe_spazio<T,P>::getCoda()
{
return componenteSpazio_coda;
}
template< class T, class P >
void classe_spazio<T,P>::setCoda(posizione posizione)
{
componenteSpazio_coda = posizione;
}
template< class T, class P >
void classe_spazio<T,P>::setTesta(posizione posizione)
{
componenteSpazio_testa = posizione;
}

/************************************************** ****************************/
/*Metodi classe lista*/
/************************************************** ****************************/
template< class T, class P >
Lista<T,P>::Lista()
{
crealista();
}

template< class T, class P >
void Lista<T,P>::crealista()
{
posizione Scoda, Stesta, temp;
Scoda = spazio.getCoda();
//Stesta = spazio.getTesta();
if(Scoda == -1)
{
cout << endl << "Errore: Non c'e' spazio per creare la nuova lista" << endl;
}
else
{
//Assegna alla coda la posizione
testa = Scoda;
//Assegno alla coda la posizione di testa perchè la lsita e' di un solo elemento
coda = testa;
spazio.setCoda(spazio[Scoda].precedente);
temp = spazio[Scoda].precedente;
spazio[Scoda].precedente = -1;
spazio[temp].successivo = -1;
}


}

template< class T, class P >
bool Lista<T,P>::listavuota() const
{
bool ritorno;
if(testa == -1)
{
ritorno = true;
}
else
{
ritorno = false;
}
return ritorno;
}

template< class T, class P >
Lista<T,P>::posizione Lista<T,P>::primoLista() const
{
return testa;
}

template< class T, class P >
Lista<T,P>::posizione Lista<T,P>::succlista(Lista::posizione p) const
{
posizione ele = -1;
/*Precondizione = La posizione passata non deve essere maggiore del
numero massimo di elementi del vettore spazio*/
if((p >= 0) && (p < N))
{
ele = spazio[p].successivo;
}
return ele;
}

template< class T, class P >
Lista<T,P>::posizione Lista<T,P>::preclista(Lista::posizione p) const
{
posizione ele = -1;
/*Precondizione = La posizione passata non deve essere maggiore del
numero massimo di elementi del vettore spazio*/
if((p >= 0) && (p < N))
{
ele = spazio[p].precedente;
}
return ele;

}

template< class T, class P >
bool Lista<T,P>::finelista(Lista::posizione p) const
{
return (p==coda);
}

tipoelem Lista::leggilista(posizione p) const
{
tipoelem ele = -1;
/*Precondizione = La posizione passata non deve essere maggiore del
numero massimo di elementi del vettore spazio*/
if((p >= 0) && (p < N))
{
ele = spazio[p].elemento;
}
return ele;
}

template< class T, class P >
void Lista<T,P>::scrivilista(tipoelem a, posizione p)
{
spazio[p].elemento = a;
/*Precondizione = La posizione passata non deve essere maggiore del
numero massimo di elementi del vettore spazio*/
if((p >= 0) && (p < N))
{
spazio[p].elemento = a;
}
}

template< class T, class P >
void Lista<T,P>::inslista(tipoelem a, Lista::posizione p)
{
/*Precondizione = La posizione passata non deve essere maggiore del
numero massimo di elementi del vettore spazio*/
if((p >= 0) && (p < N))
{
//Verifico che la lista spazio non è vuota
if(spazio.getCoda() != -1)
{
posizione NCodaSpazio;
NCodaSpazio = spazio[spazio.getCoda()].precedente;
//debug
//cout << NCodaSpazio << endl;

//Caso in cui sto aggiungendo un elemento in mezzo alla lista
if(testa != p)
{
//Setto i valori del nuovo elemento che sto inserendo in posizione p
spazio[spazio.getCoda()].elemento = a;
spazio[spazio.getCoda()].successivo = p;
spazio[spazio.getCoda()].precedente = spazio[p].precedente;
spazio[p].precedente = spazio.getCoda();
spazio[spazio[p].precedente].successivo= spazio.getCoda();
}
//Controllo se l'elemento che sto inserendo sostituirà la testa
if(testa == p)
{
testa = spazio.getCoda();
//Setto i valori del nuovo elemento che sto inserendo in posizione p
spazio[spazio.getCoda()].elemento = a;
spazio[spazio.getCoda()].successivo = p;
spazio[spazio.getCoda()].precedente = -1;
spazio[p].precedente = spazio.getCoda();
}
//Caso in cui la lista è vuota e sto inserendo il primo elemento
if(listavuota())
{
testa = spazio.getCoda();
coda = testa;
spazio[spazio.getCoda()].successivo = -1;
spazio[spazio.getCoda()].precedente = -1;
spazio[spazio.getCoda()].elemento = a;
}


spazio.setCoda(NCodaSpazio);
spazio[NCodaSpazio].successivo = -1;

}
else
{
cout << "Errore: Non c'e' spazio per inserire l'elemento" << endl;
}
}
else
{
cout << "Posizione non corretta" << endl;
}
}

template< class T, class P >
void Lista<T,P>::canclista(Lista::posizione p)
{
/*Precondizione = La posizione passata non deve essere maggiore del
numero massimo di elementi del vettore spazio*/
if((p >= 0) && (p < N))
{
//debug
//cout << "Testa: " << testa << " Coda: " << coda << " Posizione: " << p << endl;
//Cancellamento dell'elemento dalla lista*************************************
//sto cancellando un elemento centrale
if((p != testa) && (p != coda))
{
spazio[spazio[p].precedente].successivo = spazio[p].successivo;
spazio[spazio[p].successivo].precedente = spazio[p].precedente;
//debug
//cout << "Cancello un elemento centrale" << endl;
}
//sto cancellando la testa
if((p == testa) && (p != coda))
{
spazio[spazio[p].successivo].precedente = -1;
testa = spazio[p].successivo;
//debug
//cout << "Cancello la testa" << endl;
}
//sto cancellando la coda
if((p != testa) && (p == coda))
{
spazio[spazio[p].precedente].successivo = -1;
coda = spazio[p].precedente;
//debug
//cout << "Cancello la coda" << endl;
}
//la lista è di un solo elemento
if((p == testa) && (p == coda))
{
testa = -1;
coda = -1;

}
posizione temp = spazio.getCoda();
//Accodamento della cella eliminata alla lista Spazio*************************
//caso in cui la lista spazio era vuota
if(temp == -1)
{
//debug
//cout << "La lista SPAZIO era vuota" << endl;
spazio.setTesta(p);
spazio.setCoda(p);
spazio[p].precedente = -1;
spazio[p].successivo = -1;
spazio[p].elemento = 0;
}
else //tutti gli altri casi
{
spazio[temp].successivo = p;
spazio[p].precedente = temp;
spazio[p].successivo = -1;
spazio[p].elemento = 0;
spazio.setCoda(p);
}
}
else
{
cout << "Posizione non corretta" << endl;
}
}
/************************************************** ****************************/
/*Metodi classe lista*/
/************************************************** ****************************/
template< class T, class P >
int Superclasse<T,P>::lunghezza()
{
int cont=0;
posizione appo;
if(listavuota()==true)
cont=0;
else
{

appo=primoLista();

for(;finelista(appo)==false;appo=succlista(appo))
{

cont++;
}
}

return cont;
}

template< class T, class P >
void Superclasse<T,P>::inverti()
{
posizione appo_testa;
posizione appo_coda;

int index;
int lungh;
tipoelem val_appo;

if(listavuota()!=true)
{
lungh=lunghezza();
lungh=lungh/2;



appo_testa=primoLista();
appo_coda=primoLista();

for(;finelista(appo_coda)==false;appo_coda=succlis ta(appo_coda));

appo_coda = preclista(appo_coda);

for(index=0;index<lungh;index++)
{
//val_appo = spazio[appo_testa].elemento;
val_appo = leggilista(appo_testa);

//spazio[appo_testa].elemento = spazio[appo_coda].elemento;
scrivilista(leggilista(appo_coda), appo_testa);

//spazio[appo_coda].elemento = val_appo;
scrivilista(val_appo, appo_coda);

appo_testa=succlista(appo_testa);
appo_coda=preclista(appo_coda);
}
}

}
template< class T, class P >
bool Superclasse<T,P>::palindroma()
{

posizione appo_testa;
posizione appo_coda;

int index;
int lungh;
tipoelem val_appo;

bool res=true;

if(listavuota()!=true)
{
lungh= lunghezza();
lungh=lungh/2;

appo_testa=primoLista();
appo_coda=primoLista();

for(;finelista(appo_coda)==false ; appo_coda=succlista(appo_coda));

appo_coda = preclista(appo_coda);

for(index=0;index<lungh && (res==true);index++)
{
//if(spazio[appo_testa].elemento != spazio[appo_coda].elemento)
if(leggilista(appo_testa) != leggilista(appo_coda))
res=false;

appo_testa=succlista(appo_testa);
appo_coda=preclista(appo_coda);

}
}

return res;

}

Neptune
19-11-2010, 15:50
Ho provato a modificare le varie intestazioni, tipo ad esempio questa:



template< class T, class P >
posizione classe_spazio<T,P>::getTesta()


In questo modo:


template<class T, class P>
classe_spazio<T,P>::posizione classe_spazio<T,P>::getTesta()


Ma mi da comunque errore.

Ovvero mi sono accorto di aver resto privati typedef posizione e tipoelem per poter utilizzare il "template", però anche facendo questa correzione continua a darmi ugualmente gli stessi due erorri.

Neptune
19-11-2010, 16:23
Ho inserito nelle classi Lista e classe_spazio le seguenti diciture:



typedef typename Superclasse<T,P>::posizione posizione;
typedef typename Superclasse<T,P>::tipoelem tipoelem;


Le ho prese da un codice postato dal professore, a spanne dovrebbero voler dire "quando chiami posizione riferisciti al tipo posizione della classe Superclasse e passagli i parametri di template T e P".

Poi le intestazioni dei vari metodi gli ho convertiti ad esempio (prendo il solito metodo getTesta() citato prima):



template<class T, class P> typename classe_spazio<T,P>::posizione classe_spazio<T,P>::getTesta()


Utilizzando questa accortezza per tutti i metodi che non ritonavano un tipo standard ma "posizione" e "tipoelem" le librerie si compilano.

Quando però le utilizzo in un main.cpp così fatto:


#include <cstdlib>
#include <iostream>

#include "listap.h"

using namespace std;

int main(int argc, char *argv[])
{

Lista<int,int> L2;

system("PAUSE");
return EXIT_SUCCESS;
}



Si blocca in fase di linkaggio dandomi questo errore:


[Linker error] undefined reference to `Lista<int, int>::Lista()'
ld returned 1 exit status
C:\Users\Neptune\Desktop\Asd\laboratorio\10-11-2010 V.2\Lista con cursori\Makefile.win [Build Error] [Lista_con_cursori.exe] Error 1


Infatti mi fa tutti i vari file .o ma non mi crea l'eseguibile. Non so proprio cosa possa essere, se nel main levo quella dichiarazione di "L2" come per magia compila.

MItaly
19-11-2010, 16:28
Originariamente inviato da Neptune
Si blocca in fase di linkaggio dandomi questo errore:


[Linker error] undefined reference to `Lista<int, int>::Lista()'
ld returned 1 exit status
C:\Users\Neptune\Desktop\Asd\laboratorio\10-11-2010 V.2\Lista con cursori\Makefile.win [Build Error] [Lista_con_cursori.exe] Error 1


Infatti mi fa tutti i vari file .o ma non mi crea l'eseguibile. Non so proprio cosa possa essere, se nel main levo quella dichiarazione di "L2" come per magia compila.
Già ti dissi che separare dichiarazioni e definizioni nei template è un casino... leggi da qui (http://www.parashift.com/c++-faq-lite/templates.html#faq-35.12) in giù.

Neptune
19-11-2010, 16:37
Originariamente inviato da MItaly
Già ti dissi che separare dichiarazioni e definizioni nei template è un casino... leggi da qui (http://www.parashift.com/c++-faq-lite/templates.html#faq-35.12) in giù.

Il mio inglese è un pò arruginito, ma da quel che ho capito dice che:

- Il compilatore quando legge la dicitura template non fa altro che cercare tutti i nomi del template e sostituirli con il tipo che si desidera utilizzare;

- Quano intestazione e corpo del metodo/funzione sono separati il compilatore ha qualche problema nel fare questa operazione, anche se non sono riuscito ad intendere che genere di problema.

Quindi l'unica soluzione è sviluppare i metodi direttamente nella dichiarazione di classe? non ci sono scappatoie semplici?

Quindi a questo punto la mia libreria cosa diventa? Un file .h da includere e stop?

Neptune
19-11-2010, 20:31
Ma se io ho un classe che nella patre pubblica ha qualcosa del tipo:



typedef T numero;


e poi ho una seconda classe che eredita dalla prima, erediterà anche quella typedef?

E' invece possibile fare una typedef "templetizzata" ma globale? ovvero applicare un template alla typedef? o la cosa risulta insensata? :fagiano:

Un'altra cosa ho notato che se ho una typedef "templetizzata" in una classe, e poi ho un metodo che ritorna quel tipo, devo applicare la dicitura typename o mi da errore, come mai?

Ovvero:



typename Lista<T,P>::tipoelem Lista<T,P>::leggi(posizione p)


Senza la dicitura "typename" mi da gli stessi errori di prima


expected constructor, destructor, or type conversion before "classe_spazio"
expected `;' before "classe_spazio"


Perchè ha bisogno della dicitura typename?

MItaly
19-11-2010, 21:08
Originariamente inviato da Neptune
- Quano intestazione e corpo del metodo/funzione sono separati il compilatore ha qualche problema nel fare questa operazione, anche se non sono riuscito ad intendere che genere di problema.
La questione è la seguente: il codice di un template non è vero codice, ma direttive che istruiscono il compilatore su come genere il codice effettivo nel momento in cui il template viene effettivamente utilizzato (=istanziazione del template).

Le direttive in questione devono essere presenti completamente in tutte le unità di compilazione (=.cpp) in cui verrà usato il template, altrimenti il compilatore al momento dell'istanziazione del template non saprà come generarlo effettivamente. In generale infatti non è possibile contenere queste direttive nei moduli oggetto, che contengono solo codice già compilato (e i template, non essendo nemmeno ancora codice C++ "completo", non possono essere compilati se non hanno tutti i parametri già sostituiti).


Quindi l'unica soluzione è sviluppare i metodi direttamente nella dichiarazione di classe? non ci sono scappatoie semplici?
È una delle soluzioni più diffuse. I metodi in ogni caso non risulteranno inline se sono troppo grossi perché il compilatore si arrangerà a trasformarli in metodi non-inline, e gli eventuali duplicati (accade ad esempio se si usa una classe template con gli stessi parametri in due unità di compilazione diverse) vengono eliminati dal linker (per via della One Definition Rule (http://en.wikipedia.org/wiki/One_Definition_Rule), punto 3).

Una alternativa è tenere dichiarazione e definizione separate come in una classe normale, ma fare sì che esse siano sempre incluse quando si include lo header che contiene il template. Puoi o mettere tutto nel .hpp oppure tenere la dichiarazione nel .hpp, la definizione in un .tpl (o .tcc o altro ancora, non c'è molto accordo su che estensione usare, ma in generale è meglio evitare il .cpp) e #includere quest'ultimo in coda alla dichiarazione della classe template.

Esiste anche un altro metodo (spiegato nel link sopraindicato) per tenere le implementazioni in un .cpp e istanziare lì i template che si sa che serviranno, ma secondo me è molto scomodo come metodo.


Quindi a questo punto la mia libreria cosa diventa? Un file .h da includere e stop?
Sì.

(le altre risposte dopo cena :) )

MItaly
19-11-2010, 22:00
Originariamente inviato da Neptune
Ma se io ho un classe che nella patre pubblica ha qualcosa del tipo:



typedef T numero;


e poi ho una seconda classe che eredita dalla prima, erediterà anche quella typedef?

Sì, se il typedef è protected o pubblico.


E' invece possibile fare una typedef "templetizzata" ma globale? ovvero applicare un template alla typedef? o la cosa risulta insensata? :fagiano:
Parli di una typedef che lasci template uno o più parametri? È una cosa che risulta comoda ma che non è supportata dal corrente standard; verrà aggiunto invece nel prossimo standard C++.


Un'altra cosa ho notato che se ho una typedef "templetizzata" in una classe, e poi ho un metodo che ritorna quel tipo, devo applicare la dicitura typename o mi da errore, come mai?

Ovvero:



typename Lista<T,P>::tipoelem Lista<T,P>::leggi(posizione p)


Senza la dicitura "typename" mi da gli stessi errori di prima


expected constructor, destructor, or type conversion before "classe_spazio"
expected `;' before "classe_spazio"


Perchè ha bisogno della dicitura typename?
typename serve per risolvere delle ambiguità grammaticali come quella in questione, per cui il compilatore non riesce a determinare se il nome in questione si riferisce ad un tipo o ad altro. La keyword typename risolve l'ambiguità indicando esplicitamente al compilatore che l'identificatore che segue si riferisce ad un tipo.

Loading