Da quel che vedo qui i template c'entrano pochetto.
Osserva le due classi: praticamente (a meno di un dato in Sizer) hanno gli stessi metodi.
Metodi tra l'altro che vanno comodamente in overload senza dover scomodare la specializzazione dei template.
In pratica non c'è differenza tra scrivere:
codice:
template<>
void Save<float>(float& f )
{
std::cout << "write" << f << " ";
}
e
codice:
void Save(float& f)
{
std::cout << "write" << f << " ";
}
dato che una funzione specifica è la prima scelta del compilatore.
(Non ho capito il perché di float& , devi modificarlo?)
Poi quella:
codice:
template < class T, class Allocator , class U >
void Save(T& vect) etc...
mi pare una forzatura.
Primo: non hai garanzie che T sia un vector (vero che puoi forzare la specializzazione, ma è un pena farlo ogni volta);
secondo: Allocator non lo usi in quella funzione e non c'è un parametro a cui faccia riferimento;
terzo: quel parametro U ti serve solo a richiamare la funzione Save<U> forzando la specializzazione(il che come detto sopra non serve se le varie Save sono in overload).
Dando per scontato che i metodi di Writer e Sizer facciano cose diverse ed è necessario usare un container (che non sia per forza un vector) e basandosi sul codice che hai mostrato, io farei così:
codice:
class CBase {
public:
virtual ~CBase() {}
virtual void Save(float& )=0;
virtual void Save(std::string& )=0;
// eventuali altri overload = 0;
// intercetta tutte le chiamate a Save non specializzate
// dagli overload precedenti.
template <typename T>
void save(T& ) {}
// elabora i dati provenienti da un container
template <typename Iterator>
void Save(Iterator a, Iterator z) {
while (a != z) {
this->Save(*a);
++a;
}
}
};
class Writer : public Cbase {
public:
// costruttori, distruttori etc.
virtual void Save(float& ) { /* fa qualcosa */ }
virtual void Save(std::string& ) { /* fa qualcosa */ }
// eventuali altri overload { /* fanno qualcosa */ }
};
class Sizer : public Cbase {
public:
// costruttori, distruttori etc.
virtual void Save(float& ) { /* fa qualcosa */ }
virtual void Save(std::string& ) { /* fa qualcosa */ }
// eventuali altri overload { /* fanno qualcosa */ }
};
int main (etc) {
std::vector<float> v;
v.push_back(1.3f);
v.push_back(1.4f);
Sizer w;// o Writer w
w.Save(v.begin(), b.end());
float f = 1.2f;
w.Save(f);
std::cout << w.m_size_of_buffer;
// esempio 2 con un container
CBase* cb[2];
cb[0] = new Sizer;
cb[1] = new Writer;
cb[0]->Save(v.begin(), b.end());
cb[1]->Save(v.begin(), b.end());
cb[0]->Save(f);
cb[1]->Save(f);
delete cb[0];
delete cb[1];
return 0;
}
Ora (e mi riallaccio anche a un tuo post passato), la classe CBase non è un'interfaccia pura come intesa in Java, però i dati della funzione Save non fanno nessun riferimento a dati membro di CBase, ma solo a dati passati alla funzione. Tutti gli altri metodi sono virtuali puri, pertanto CBase si può considerare una interfaccia pura a tutti gli effetti.
In pratica la Save di CBase è solo una funzione adapter che richiamerà la funzione specializzata (se presente) delle classi derivate.
Inoltre lavorando solo con iteratori ti svincoli dall'obbligo di usare solo dei vector, ma puoi usare anche list o deque.
Nota che così facendo ti sei anche sganciato da Allocator e dal dover specializzare Save forzando il parametro template.