Newsgroup: it.comp.lang.c++
Titolo: È possibile usare un typename come un template?

Salve a tutti
stavo cercando di scrivere del codice, quando ho "prodotto" questo errore:
$ g++ -W -Wall -std=c++98 -pedantic errore.cc
errore.cc: In function ‘void f()’:
errore.cc:6: error: ‘C’ is not a template
errore.cc:6: error: ‘C’ is not a template
errore.cc: In function ‘int main()’:
errore.cc:11: error: no matching function for call to ‘f()’
$ cat errore.cc -n
     1  template <typename T> class Class {
     2      T member;
     3  };
     4
     5  template <typename C, typename T> void f() {
     6      C<T>* var = new C<T>();
     7      delete(var);
     8  }
     9
    10  int main() {
    11      f<Class, int>();
    12  }
$
Non posso "passare" una classe templatica a una funzione templatica, e
istanziarla dentro la funzione stessa? (questo e' un caso semplificato,
dovrei lavorare con una serie di classi templatiche simili a Class)
-- 
By ZeD

 istanziarla dentro la funzione stessa?
Puoi farlo, ma devi dichiarare il parametro come template.
 template<  template< typename T_ > class C, typename T > void f()
 {      ...
 } 
Max

 Apx.
Pensa ai templates come delle macro del preprocessore, con pero'
l'aggiunta di un controllo sul tipo.
Non mi dire che le macro del preprocessore sono piu' belle!


Ecco un'esempio in cui i "template template parameters" sono utili.
L'esempio è un po' lungo ma è completo e compila. Dipende dalla
libreria boost timer ma puoi tranquillamente eliminare il codice
relativo. L'ho inserita solo per rendere l'esempio un po' più
interessante.
// INIZIO CODICE
#include "boost/progress.hpp"
#include <vector>
#include <deque>
#include <algorithm>
#include <iterator>
#include <memory>
template < typename T >
T randGen(){
	return static_cast<T>( rand() );
}
template < typename T, typename container >
void timingA(unsigned long size){
	using std::generate_n;
	using std::back_inserter;
	using std::sort;
	using boost::progress_timer;
	progress_timer timer;
	container cont;
	generate_n( back_inserter(cont),
		size,
		randGen<T> );
	sort( cont.begin(),cont.end());
}
template < typename T, template < typename U, typename ALLOC > class
container >
void timingB(unsigned long size){
	using std::generate_n;
	using std::back_inserter;
	using std::sort;
	using std::allocator;
	using boost::progress_timer;
	progress_timer timer;
	container<T> cont;
	generate_n( back_inserter(cont),
            size,
	     randGen<T> );
	sort( cont.begin(),cont.end());
	// Qui si può usare container<U> con altri tipi
	// che non siano T
	container<std::size_t> sztCont;
	container<float> fltCont;
}
int main()
{
  using std::vector;
  using std::deque;
  const unsigned long dimension(30);
  timingA<int, vector<int> >(dimension);
  timingA<double, vector<double> >(dimension);
  timingA<int, deque<int> >(dimension);
  timingA<double, deque<double> >(dimension);
  // Errore!
  //timingA<int, vector<double> >(dimension);
  // Eliminata la ripetizione
  // del primo parametro template
  timingB<int, vector>(dimension);
  timingB<double, vector>(dimension);
  timingB<int, deque >(dimension);
  timingB<double, deque >(dimension);
  return 0;
}
// FINE CODICE
Ci sono un po'di cose da notare.
Nella dichiarazione della funzione timingB, stiamo dichiarando che uno
dei parametri template della funzione (container) è a sua volta un
template. Inoltre stiamo dichiarando che container accetta due
parametri template.
Questa dichiarazione permette di avere a disposizione, all'interno
della funzione, la dichiarazione di container per poterla istanziare
come più ci piace.
Questo non è possibile all'interno della funzione timingA.
Nel main si può vedere che per istanziare timingA sia necessario
specificare due volte il parametro T "timingA<int, vector<int> >".
Questo può portare, più facilmente, a commettere errori.
Un bell'esempio di impiego di "template template parameters" si trova
nel libro di Andrei Alexandrescu "Modern C++ Design" nel capitolo
"Policy based design".
Ciao, Ugo

Ugo Mendes Donelli ha scritto:
...
Ne approfitto per puntualizzare una cosa (e soprattutto chiedere un 
parere perchè è una cosa che non ricordo bene)
Da qualche parte mi sembra di aver letto che in realtà non è detto che i 
container std debbano per forza avere due parametri template (se non 
sbaglio lo standard lascia libertà all'implementatore)
Nel tuo esempio dichiari
template < typename T, template < typename U, typename ALLOC > class 
container >
assumendo che il parametro container abbia a sua volta due parametri.
Ma se nella mia implemmentazione un container prendesse ad esempio 3 
argomenti anzichè i due "canonici" sarei fregato.
Questo purtroppo rende i template template parameters un po meno utili 
di quello che ci si potrebbe aspettare...
E' corretto quello che ho detto? (o mi ricordo male e sto dicendo caxxate)
GM

 E' corretto quello che ho detto? (o mi ricordo male e sto dicendo caxxate)
Controllerò sullo standard il discorso dei parametri template dei
containers.
Ciao, Ugo

 
Ok se ho 5 min provo a guardarci anch'io (anche se per capirci qualcosa 
nello std bisogna scavare temo più di 5 minuti)


In effetti è previsto nel nuovo standard del C++ "c++0x".
Si chiama "Variadic Templates"
http://en.wikipedia.org/wiki/C%2B%2B0x#Variadic_templates
Attento che alla prima lettura fa venir mal di testa :-)
Ugo

Max M. wrote:

uuh, vero, che sciocco a non pensarci! >_< (anche se ora inizio a ricordare
quanto fossero brutti da leggere!)
-- 
By ZeD

...piccolo fix
è necessario cambiare la riga
	container< T > cont;
in
	container< T, std::allocator< T > > cont;
a questo punto il VC 9.0 compila ed esegue correttamente, viceversa dà
main.cpp(33) : error C2976: 'std::vector' : too few template arguments
dovrebbe dipendere dal fatto che nello scope della funzione timingB
non è noto il valore di default per il secondo parametro attuale di
template
vi torna?
M.