PDA

Visualizza la versione completa : [C++] Programmazione generica: i traits


giuseppe500
23-07-2011, 21:44
ciao.
E' circa una settimana che sto cercando di studiare un po di programmazione generica , è molto affascinante e sto facendo delle prove anche al lavoro.

Una delle cose piu' importanti che non riesco a capire sono i traits.
prendiamo per esempio questo codice , tratto dal libro modern c++ design Andrei Alexandrescu , di cui ho capito l' 1% per adesso.



template <typename T, bool isPolymorphic>
struct NiftyContainerValueTraits
{
typedef T* ValueType;
};
template <typename T>
struct NiftyContainerValueTraits<T, false>
{
typedef T ValueType;
};
template <typename T, bool isPolymorphic>
class NiftyContainer
{
typedef NiftyContainerValueTraits<T, isPolymorphic> Traits;
typedef typename Traits::ValueType ValueType;
};


il typename usa Traits::ValueType come un tipo e dichiara un typedef(cioè un alias di tipo), ValueType da quello che ho capito adesso si puo utilizzare come un tipo.
e la stessa cosa er Traits.
ma in che modo lo posso cambiare?
richiamando


typedef NiftyContainerValueTraits<T, isPolymorphic> Traits;



perchè non ho ben capito neanche lo scopo , a cosa servono di preciso? ad avere dei tipi diversi a seconda dell' occorrenza?
è un metodo a me nuovo per il passaggio di paramtri penso di aver capito ,e a me servirebbe nel codice che sto scrivendo.



ps.
è tutto oggi che giro su google ma si trovano delle dimostrazioni un po criptiche , ed essendo alle prime armi chiedo a qualcuno di voi un aiutino.
grazie.

shodan
23-07-2011, 22:29
La meta programmazione template non è semplicissima, anche perché non hai strumenti di debug a disposizione (c'è lo static assert ma solo in C++x11 e nei compilatori che lo supportano).
Per comprendere quel codice è meglio partire da qualcosa di semplice come questo e tenere bene a mente che il compilatore sceglie il tipo più adatto nell'istanziare un template:


template <typename T> struct IsChar {
static const bool value = false;
};

template <> struct IsChar<char> {
static const bool value = true;
};

cout << IsChar<int>::value << endl; // false
cout << IsChar<long>::value << endl; // false
cout << IsChar<char>::value << endl; // true

Il trucco funziona così: prima di definisce un template generico per tutti i casi, poi si specializza il template per il tipo che s'intende discriminare. In questo esempio se si istanza IsChar con un long, il compilatore sceglierà il template generico, stessa cosa per l'int. Quando si istanza il template con un char, il compilatore nota che esiste una specializzazione apposta per il char e sceglierà questa specializzazione perché è la più adatta.
Se il tipo con cui istanzi il template è un generico tipo T, puoi essere sicuro che se questo tipo T è un char, il compilatore sceglierà la specializzazione di IsChar per il tipo char, non la definizione generica del template.

Se hai capito questo, il codice postato da te diventa semplice.
Il compilatore riceve questa definizione:


template <typename T, bool isPolymorphic>
class NiftyContainer
{
typedef NiftyContainerValueTraits<T, isPolymorphic> Traits;
typedef typename Traits::ValueType ValueType;
};

dove T è sconosciuto e isPolymorphic è il risultato di un traits come nell'esempio precedente.
A seconda del valore booleano di isPolymorphic, il compilatore sceglierà quello che si adatta meglio. Se true sceglierà la definizione generica del template NiftyContainerValueTraits e ValueType sarà un puntatore, se false sceglierà la specializzazione del template NiftyContainerValueTraits e ValueType non sarà un puntatore.
Usi un typedef per effettuare un "forward" del ValueType di NiftyContainerValueTraits a NiftyContainer e il gioco è fatto.

giuseppe500
24-07-2011, 00:58
avevo letto un po sulla specializzazione dei template ma non collegavo le due cose. ho finalmente capito .
Grazie 1000 come sempre.

Loading