Tieni sempre presente che un functor riceve un tipo specifico, quindi se i muri, colonne (etc) non hanno una comune classe base, sei costretto quantomeno a scrivere diversi operatori() (uno per tipo), o ricorrere a una funzione template.
Quando dico che i due tipi di programmazione sono complementari, mi riferisco a:
Sono tutti template (quindi generici), ma ospitano un tipo polimorfo. Le due tipologie si integrano.codice:class Base{}; vector<base*> list<base*> deque<base*> std::array<base*> shared_prtr<base>
Venendo al tuo problema.
E' scontato che ci debbano essere dei vincoli. Il codice di rotazione dev'essere lo stesso per ogni oggetto non polimorfo che passi al functor, se solo uno di essi richiede codice aggiuntivo, si perde in genericità.
Altresi è scontato che i container di provenienza di questi oggetti debbano essere diversi.
Detto questo, il codice del functor (quello dentro l'operatore () per capirsi) puoi scriverlo anche in un file cpp, però i tipi devono essere esplicitati.
Il problema è che se devi aggiungere dei tipi, devi rimettere mano alla classe. Un approccio un pò pesante per la manutenzione.codice:// file .h struct RotateFunctor { ReturnType1 operator()(const muro& t); ReturnType2 operator()(const colonna& t); ReturnType3 operator()(const pilastri& t); ReturnType4 operator()(const solai& t); }; // file .cpp ReturnType1 RotateFunctor::operator()(const muro& t) { ... } ReturnType2 RotateFunctor::operator()(const colonna& t) { ... } ReturnType3 RotateFunctor::operator()(const pilastri& t) { ... } ReturnType4 RotateFunctor::operator()(const solai& t) { ... }
Come soluzione si può pensare a un functor con funzione template. Si guadagna in genericità, ma va tutto nel file.h.
Il vincolo però è che il codice nella funzione template è lo stesso per tutti i tipi.codice:struct RotateFunctor { template <typename T> ReturnType1 operator()(const T& t) { ... } };
La terza soluzione (forse la migliore dal mio punto di vista) è sfruttare la specializzazione dei template.
A compile time, il compilatore richiamerà la specializzazione corretta e la userà nell'algoritmo di rotazione. Nel functor specializzato, inoltre si può inserire codice specifico per singola classem si possono anche aggiungere funzioni solo in quel functor specializzato etc.codice:template <typename T> struct RotateFunctor { ReturnType1 operator()(const T& t) { ... } }; template <> struct RotateFunctor <muri> { ReturnType1 operator()(const muri& t) { ... } }; template <> struct RotateFunctor <colonne> { ReturnType1 operator()(const colonne& t) { ... } }; // etc.
Le implementazioni possono essere inserite in un file cpp (bisogna però vedere se ne vale la pena), e lasciare solo i prototipi dei functor specializzati nel file .h (insieme a tutto il template generico ovviamente).
Se ti serve aggiungere un functor specifico, basta specializzare quello generico e ricompilare il programma, senza modificare altro codice.
Dulcis in fundo, puoi anche costringere a dover specificare il template.
Se si tenta di usare il functor non specializzato, si avrà errore di compilazione.codice:template <typename T> struct RotateFunctor;

Rispondi quotando