Newsgroup: it.comp.lang.c++
Titolo: Aiuto con templates

Allora, sto cercando di imparare i templates ma continuo a non
riuscire bene nel test.
Esempio: una classe tipo stack con i metodi push() e pop().
- push() restituisce un int per indicare se l'operazione è andata a
buon fine o meno, ed accetta un puntatore all'oggetto da pushare;
- pop() non accetta nulla e restituisce un puntatore all'oggetto.
test.h
--------
#include <iostream>
#ifndef MYCLASS_H
#define MYCLASS_H
#define MAXELEMENTS 30
template <class T>
class MyClass
{
  public:
    MyClass();
   ~MyClass();
    int push(T *item);
    T *pop();
  private:
    int count;
    T *objects;
};
#endif
test.cpp
------------
#include "inc/test.h"
template <class T>
MyClass<T>::MyClass(void)
{
  count = 0;
  objects = new T[MAXELEMENTS];
}
template <class T>
MyClass<T>::~MyClass(void)
{
  delete objects;
}
template <class T>
int MyClass<T>::push(T *item)
{
  if (count < MAXELEMENTS)
  {
    objects[count] = item;
    count++;
  }
  else return 0;
}
prova.cpp
-------------
#include <iostream>
#include "test.h"
int main()
{
  MyClass <int> c;
  return 0;
}
provo a compilare:
g++ prova.cpp test.cpp -o prova.exe
ottengo questo:
C:\DOCUME~1\PC16\IMPOST~1\Temp/ccIVdaaa.o(.text+0x131):prova.cpp:
undefined reference to `MyClass<int>::MyClass()'
C:\DOCUME~1\PC16\IMPOST~1\Temp/ccIVdaaa.o(.text+0x13c):prova.cpp:
undefined reference to `MyClass<int>::~MyClass()'
collect2: ld returned 1 exit status
Che vuol dire? Dove sbaglio?
Se il costruttore lo includo nella dichiarazione del template in
questo modo:
template <class T>
class MyClass
{
  public:
  MyClass::MyClass()
  {
    count = 0;
    objects = new T[MAXELEMENTS];
  }
  MyClass::~MyClass()
  {
    delete[] T;
  }
  ecc. ecc.
};
in questo caso funziona.

#include "test.h"
Qui devi includere test.cpp non test.h che gi� a sua volta � stato incluso 
in test.cpp 

 in test.cpp
Ma sto compilando insieme sia prova.cpp che test.cpp.
Il file test.cpp contiene le implementazioni di quello che ho
dichiarato in "inc/test.h" e quindi il codice vero e proprio.
Il file prova.cpp deve includere (come ho sempre fatto) il file header
per aver chiare le dichiarazioni.
Sarà poi il linker ad unire i due codici oggetto generati...

Sar� poi il linker ad unire i due codici oggetto generati...
Io ho copiato il tuo esempio diviso in tre file come hai fatto tu e ottenevo 
lo stesso errore.
Mi � stato sufficiente modificare quell'include per risolvere il problema.
Ciao 

 Ciao
Comunque ho scoperto che per i template bisogna tenere tutto il codice
in un unico file, non conviene separarli.
Chiaro che includendo il file.cpp (che in clude il file.h) risolvi il
problema, ma secondo me si genera confusione; uno è tentato di
compilare tutti i file .cpp a disposizione, quando invece a 'sto punto
conviene tenere i template dichiarati e sviluppati direttamente nei
file header, così si risolve ogni problema.
L'unica cosa che continuo a non capire è come mai io debba per forza
di cose sviluppare i constructor ed i destructor all'interno stesso
della classe, perchè se minimo minimo sviluppo il constructor
all'esterno della classe non mi compila più.

all'esterno della classe non mi compila pi�.
Ma nel tuo caso, se la definizione del ctor e del dtor la scrivi all'interno 
della classe vuol dire che le hai scritte in test.h che viene incluso in 
prova.cpp, quindi � chiaro che funzioni. Se li definisci in test.cpp e non 
includi test.cpp in prova.cpp non vengono viste.
Comunque ho provato a compilare test.cpp e a linkarlo successivamente, ed in 
effetti essendo template le classi, i ctor e il dtor non vengono visti, 
mentre rendendo la classe semplice (non template insomma) ha funzionato 
correttamente. 

 includi test.cpp in prova.cpp non vengono viste.
Infatti credevo anche io che fosse così, ed ho fatto la contro-prova.
Ho messo tutto (dichiarazione ed implementazione) nel file test.h
Funziona.
Se però sposto il constructor ed il destructor _fuori_ dalla
dichiarazione tella classe, come l'implementazione di qualsiasi altra
funzione-membro della classe, non funziona più.
Sembra quasi che constructor e destructor debbano essere
obbligatoriamente implementati all'interno della classe-template.
Cosa che ovviamente non accade con una classe semplice.
Per dirti:
file "inc/test.h"
-----------------
#define MAXELEMENTS 30
template <class T>
class MyClass
{
  public:
    MyClass()
    {
      count = 0;
      objects = new T[MAXELEMENTS];
    };
   ~MyClass()
    {
      delete[] objects;
    };
    int push(T item);
    T pop();
  private:
    int count;
    T *objects;
};
Se con questa dichiarazione del template provo a spostare all'esterno
l'implementazione del constructor in quest'altro modo:
template <class T>
MyClass<T>::MyClass()
{
  /* code here */
}
allora non funziona più. Appena provo ad istanziare un oggetto di tipo
MyClass mi vien fuori l'errore di compilazione che non trova (ad
esempio) un constructor <int>.


prova a inserire nel file test.cpp questa riga
template class  MyClass<int> ;

 template class  MyClass<int> ;
Cosi funziona per� la funzione Push va modificata:
template <class T>
int MyClass<T>::push(T *item)
{
  if (count < MAXELEMENTS)
  {
    objects[count] = item;  // diventa objects[count] = *item;
    count++;
  }
  else return 0;
}
Ciao 

 
No. Non � necessario che l'implementazione sia nel corpo della definizione
della classe. Funziona cos�. Per istanziare una funzione template o una
funzione (o ctor/dtor) di una classe template si deve avere visibilit� sul
codice dell'implementazione. Se una certa unit� di compilazione usa una
funzione (o ctor/dtor) di una certa istanziazione di una classe template e
non ha visibilit� sull'implementazione, quella funzione non sar� ivi
istanziata. Quindi, se metti le implementazioni template in unit� di
compilazione separate, nessuno sapr� come istanziarle, n� potranno essere
istanziate in quelle unit� di compilazione, giacch� l� non � normalmente
possibile sapere per quali tipi si vuole usare un certo template. Se si
richiede un'istanziazione esplicita (come suggeriva xnoise), si ottiene
l'effetto desiderato, ma solo per il tipi per i quali si � richiesta
l'istanziazione.
Ad ogni modo, gran parte della libreria standard � basata su classi e
funzioni template. Dai uno sguardo ai sorgenti. Se usi le stesse
convenzioni per distribuire dichiarazioni e definizioni, non sbagli.
Max

 Max
Vabè ma tornando al mio esempio quassù, io ho la definizione del
template nel file header.
L'implementazione nel file cpp compila ma se tento poi di utilizzare
l'oggetto, nisba.

 l'oggetto, nisba.
Nel tuo primo esempio, prova.cpp instanzia un oggetto di tipo MyClass<int>.
Ma l'implementazione di ctor e dtor di MyClass<T> non � visibile in
prova.cpp. Quindi vale quello che ho detto nel mio precedente messaggio:
prova.cpp non pu� istanziare il ctor o il dtor, e il linker non li trova,
giacch� nessun altro li istanzia.
Max