PDA

View Full Version : [C++] Template function dentro a una classe


vendettaaaaa
21-05-2013, 22:10
Ciao,
ho una classe che contiene questi typedef:

classe MyVector
{
public:
// ****************** Typedefs for the user
typedef double value_type;
typedef double& reference;
typedef const double& const_reference;
typedef double* iterator;
typedef const double* const_iterator;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
};

Ho un costruttore che costruisce l'oggetto (un vettore di double) partendo da un array di double:
MyVector(size_type n, const double* array);
che sfrutta la seguente funzione privata:
// Private function for memory management: create a BbVector by copying another BbVector using iterators (similar to one of std::vector constructors)
void MyVector::create(const_iterator b, const_iterator e)
{
mySize = e - b;
myVector = alloc.allocate(mySize + 1);
start = myVector + 1;
limit = avail = start + mySize;

uninitialized_copy(b, e, start);
}

dove alloc è uno std::allocator<double>.

Ho un altro costruttore, che prende uno std::vector:
BbVector::BbVector(const vector<double>& vec)
{
size_type n = vec.size();

if (n == 0)
{
create();
}
else
{
create(vec.cbegin(), vec.cend());
}
}

Purtroppo questo costruttore non compila: la funzione create prende in input un const double*, mentre qui gli passo un std::vector<double>::const_iterator, cioè
typedef __gnu_cxx::__normal_iterator<const_pointer, vector> const_iterator, con tutti i typedef del caso...

Allora ho pensato di rendere create una funzione template, scrivendo:
template<class C>
void BbVector::create(typename C::const_iterator b, typename C::const_iterator e)
{
mySize = e - b;
myVector = alloc.allocate(mySize + 1);
start = myVector + 1;
limit = avail = start + mySize;

uninitialized_copy(b, e, start);
}

Funziona, solo per il vector, infatti non posso più scrivere:
create<const double*>(array, array + n);
giustamente, visto che il tipo const double* non ha un membro chiamato const_iterator...

L'unico modo per farla funzionare quando la invoco passandogli l'array di double è:
create<MyVector>(array, array + n);
però mi sembra un po' astruso instanziare un template, definito dentro alla mia classe, usando la classe stessa...
Qualcuno sa suggerirmi una soluzione migliore? Ho appena cominciato a studiare i template in modo approfondito, e sono un po' spaesato :D

balth@zar
21-05-2013, 23:18
Allora ho pensato di rendere create una funzione template, scrivendo:
template<class C>
void BbVector::create(typename C::const_iterator b, typename C::const_iterator e)
{
mySize = e - b;
myVector = alloc.allocate(mySize + 1);
start = myVector + 1;
limit = avail = start + mySize;

uninitialized_copy(b, e, start);
}

Funziona, solo per il vector, infatti non posso più scrivere:
create<const double*>(array, array + n);


Ma se fai il template sul tipo di iteratore anzichè sulla classe che definisce il tipo di iteratore non funziona?
Cioè

template <typename T>
void BbVector::create(T b, T e) {
//bla bla bla
}


e poi la chiami con

create<const double*>(...)
create<std::vector<double>::const_iterator>(...)

vendettaaaaa
21-05-2013, 23:59
Ma se fai il template sul tipo di iteratore anzichè sulla classe che definisce il tipo di iteratore non funziona?
Cioè

template <typename T>
void BbVector::create(T b, T e) {
//bla bla bla
}


e poi la chiami con

create<const double*>(...)
create<std::vector<double>::const_iterator>(...)
Se la scrivessi così non dovrei specificare gli argomenti del template come dici. Vedi ad esempio std::swap.
Cmq non mi pare la soluzione giusta in quanto non avrei controllo su cosa viene passato alla funzione. Quei T finiscono a std::uninitialized_copy, quindi devono essere dei puntatori. Se però scrivo il template in questo modo, non ho alcun filtro: potrei scrivere
bool a, b;
create(a, b); e il tutto compilerebbe (posto che la userei solo io, in quanto private, e in quanto solo io uso questa semplice classe :D ma qui voglio capire la teoria!). A quanto ho capito, uno dei punti di forza dei template è la possibilità di eseguire molti check a compile time, quindi credo che andrebbe fatto in un altro modo :fagiano:

balth@zar
22-05-2013, 08:51
Quei T finiscono a std::uninitialized_copy, quindi devono essere dei puntatori. Se però scrivo il template in questo modo, non ho alcun filtro: potrei scrivere
bool a, b;
create(a, b); e il tutto compilerebbe

Non ho provato ma non dovrebbe compilare proprio perchè non compilerebbe unitialized_copy!

vendettaaaaa
22-05-2013, 09:07
Non ho provato ma non dovrebbe compilare proprio perchè non compilerebbe unitialized_copy!
Giusto :doh: Tosti i template :D

balth@zar
22-05-2013, 10:07
Giusto :doh: Tosti i template :D

In generale puoi fare un controllo a compile time tramite una classe "traits" che definisce le caratteristiche che deve avere la tua classe parametro.
In questo caso uninitialized_copy utilizza iterator_traits che è naturalmente definita per gli iteratori ed ha una specializzazione per i tipi puntatore (T* e const T*)
Se volessi usare un'altro tipo di iteratore che non rientra nelle casistiche predefinite basterebe scrivere una tua specializzazione di iterator_traits.

vendettaaaaa
22-05-2013, 10:31
In generale puoi fare un controllo a compile time tramite una classe "traits" che definisce le caratteristiche che deve avere la tua classe parametro.
In questo caso uninitialized_copy utilizza iterator_traits che è naturalmente definita per gli iteratori ed ha una specializzazione per i tipi puntatore (T* e const T*)
Se volessi usare un'altro tipo di iteratore che non rientra nelle casistiche predefinite basterebe scrivere una tua specializzazione di iterator_traits.
Hmm...in linea teorica ho capito...ora devo fare. Se tutto va bene oggi mi arriva TCPP Programming Language di Stroustrup (nuova edizione col C++11 :D ), quindi mi vado a leggere subito l'argomento, poi ci provo!