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:
codice:
class Base{};
vector<base*>
list<base*>
deque<base*>
std::array<base*>
shared_prtr<base>
Sono tutti template (quindi generici), ma ospitano un tipo polimorfo. Le due tipologie si integrano.
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.
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) { ... }
Il problema è che se devi aggiungere dei tipi, devi rimettere mano alla classe. Un approccio un pò pesante per la manutenzione.
Come soluzione si può pensare a un functor con funzione template. Si guadagna in genericità, ma va tutto nel file.h.
codice:
struct RotateFunctor {
template <typename T>
ReturnType1 operator()(const T& t) { ... }
};
Il vincolo però è che il codice nella funzione template è lo stesso per tutti i tipi.
La terza soluzione (forse la migliore dal mio punto di vista) è sfruttare la specializzazione dei template.
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.
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.
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.
codice:
template <typename T>
struct RotateFunctor;
Se si tenta di usare il functor non specializzato, si avrà errore di compilazione.