Sono equivalenti praticamente? Mi piacerebbe conoscere la differenza pratica tra i modelli.
Sono diversi concettualmente.
Per il primo schema, i decoratori seguono (in modo trasparente) una classe di funzionalità di base, ognuno dei quali aggiunge la propria torsione/specializzazione a un'implementazione esistente.
Il rapporto dei primi modelli modello è "is-a" (MyTypeWithMixins
è una specializzazione Mixin1<MyType>
, Mixin1<MyType>
è una specializzazione MyType
).
Questo è un buon approccio quando si implementano funzionalità all'interno di un'interfaccia rigida (poiché tutti i tipi implementeranno la stessa interfaccia).
Per il secondo modello, si dispone di parti di funzionalità utilizzate come dettagli di implementazione (possibilmente all'interno di classi diverse non correlate).
Il rapporto modellato ecco "è implementato in termini di" (MyTypeWithMixins
è una specializzazione MyType
, implementato in termini diMixin1
e Mixin2
funzionalità). In molte implementazioni CRTP, la base basata su CRTP è ereditata come privata o protetta.
Questo è un buon approccio quando si implementano funzionalità comuni su diversi componenti indipendenti (ad esempio, non con la stessa interfaccia).Questo perché due classi che ereditano da Mixin1 saranno non con la stessa classe base.
Per fornire un esempio concreto per ciascuna:
Per il primo caso, considerare la modellazione di una libreria GUI. Ogni controllo visivo avrebbe una (per esempio) funzione display
, che in uno ScrollableMixin aggiungerebbe barre di scorrimento, se necessario; Il barre di scorrimento mixin sarebbe una classe base per la maggior parte dei controlli che vengono ri-considerevole (ma tutti una parte della gerarchia "controllo/componente visivo/visualizzabile" classe.
class control {
virtual void display(context& ctx) = 0;
virtual some_size_type display_size() = 0;
};
template<typename C>class scrollable<C>: public C { // knows/implements C's API
virtual void display(context& ctx) override {
if(C::display_size() > display_size())
display_with_scrollbars(ctx);
else
C::display(canvas);
}
...
};
using scrollable_messagebox = scrollable<messagebox>;
In questo caso, tutti i tipi mixin sostituirà (ad esempio) un metodo di visualizzazione e delegherà parti della sua funzionalità (la parte di disegno specializzata) al tipo decorato (la base)
Per il secondo caso, considerare un caso in cui si implementerebbe un sistema interno per aggiungere un numero di versione agli oggetti serializzati all'interno dell'applicazione.L'implementazione sarebbe simile a questa:
template<typename T>class versionable<T> { // doesn't know/need T's API
version_type version_;
protected:
version_type& get_version();
};
class database_query: protected versionable<database_query> {};
class user_information: protected versionable<user_information> {};
In questo caso, sia database_query
sia user_information
memorizzano le loro impostazioni con un numero di versione, ma non sono in alcun modo nella stessa gerarchia di oggetti (non hanno una base comune).
Questo non è ciò che CRTP è per. Certo, CRTP coinvolge la struttura generale 'classe Child: Parent', ma il punto intero è che la classe base - 'Parent ', conosce la classe figlio' Child' (come? È un parametro template!) Parent può quindi fai riferimento alle cose definite nel bambino stesso - ad esempio, può creare un operatore! = dall'operatore della classe figlio ==. –
@HWalters, non necessariamente. Nell'esempio che ho fornito sopra, l'implementazione di 'versionable' non impone alcuna restrizione su 'T' (cioè, non ha bisogno di sapere nulla a riguardo). Questo è stato fatto apposta. –
utnapistim
Ho sbagliato; usando 'Child' per istanziare' Parent' semplicemente come un univoco conveniente per il tipo di base ha utilità. Ma penso che abbiate sostenuto il punto sbagliato: non imporre restrizioni su "T" è insufficiente per giustificare l'uso del CRTP. In particolare, lotto con il tuo esempio; il risultato finale è che gli oggetti 'database_query' e' user_information' hanno membri version_ e get_version, anche dello stesso tipo. Quindi quale vantaggio viene trasmesso utilizzando il CRTP per iniettarli? (Cioè, certo, le classi base sono _ diversi tipi, ma in che modo utile sarebbe quella?) –