Domanda

Sto usando i modelli C ++ per passare i funzione di strategia per cambiare il comportamento della mia funzione. Funziona bene Il functor che passo è una classe senza stato senza memoria e sovraccarica l'operatore () nel classico modo di funzione.

template <typename Operation> int foo(int a) 
{
int b=Operation()(a);
/* use b here, etc */
}

Lo faccio spesso, e funziona bene, e spesso sto realizzando modelli con 6 o 7 funzioni di template passati!

Comunque mi preoccupo sia dell'eleganza del codice sia dell'efficienza. Il functor è senza stato, quindi presumo che il costruttore Operation () sia gratuito e la valutazione del functor sia efficiente quanto una funzione incorporata, ma come tutti i programmatori C ++ ho sempre qualche fastidioso dubbio.

La mia seconda domanda è se potrei usare un approccio con funzione alternativa. Uno che non sovrascrive l'operatore (), ma fa tutto nel costruttore come effetto collaterale! Qualcosa del tipo:

struct Operation {
  Operation(int a, int &b) { b=a*a; }
};
template <typename Operation> int foo(int a) 
 {
   int b;
   Operation(a,b);
    /* use b here, etc */
 }

Non ho mai visto nessuno usare un costruttore come " lavoro " di un funzione, ma sembra che dovrebbe funzionare. C'è qualche vantaggio? Qualche svantaggio? Mi piace la rimozione della strana parentesi raddoppiata "Operator () (a)" , ma probabilmente è solo estetico.

È stato utile?

Soluzione

  

Qualche svantaggio?

  • I medici non restituiscono alcun valore utile - non possono essere utilizzati nelle chiamate concatenate (ad esempio foo (bar ()).
  • Possono lanciare.
  • Punto di vista della progettazione: i medici sono funzioni di creazione di oggetti, non pensati per essere cavalli da lavoro.

Altri suggerimenti

  1. I compilatori incorporano effettivamente il costruttore vuoto di Operation (almeno gcc in situazioni simili, tranne quando hai disattivato l'ottimizzazione)
  2. Lo svantaggio di fare qualsiasi cosa nel costruttore è che non è possibile creare un funzione con un certo stato interno in questo modo - ad es. funzione per il conteggio del numero di elementi che soddisfano un predicato. Inoltre, l'utilizzo di un metodo di un oggetto reale come un funzione ti consente di memorizzarne l'istanza per un'esecuzione successiva, cosa che non puoi fare con il tuo approccio costruttore.

Da un punto di vista delle prestazioni il codice dimostrato viene completamente ottimizzato con VC e GCC. Tuttavia, una strategia migliore è spesso quella di prendere il functor come parametro, in questo modo si ottiene molta più flessibilità e caratteristiche di prestazione identiche.

Consiglio di definire un funzione che funzioni con i contenitori STL, ovvero che dovrebbero implementare operator (). (Seguire l'API della lingua che stai usando è sempre una buona idea.)

Ciò consente ai tuoi algoritmi di essere molto generici (passa in funzioni, funzioni, stl-bind, boost :: function, boost :: bind, boost :: lambda, ...) che è ciò che si desidera di solito.

In questo modo, non è necessario specificare il tipo di funzione come parametro del modello, basta costruire un'istanza e passarla in:

my_algorithm(foo, bar, MyOperation())

Non sembra utile implementare il costruttore in un'altra classe.
Tutto quello che stai facendo è rompere l'incapsulamento e impostare la tua classe per abuso.

Il costruttore dovrebbe inizializzare l'oggetto in un buono stato come definito dalla classe. Stai permettendo a un altro oggetto di inizializzare la tua classe. Quali garanzie hai che questa classe di template sappia come inizializzare correttamente la tua classe? Un utente della tua classe può fornire qualsiasi oggetto che potrebbe interferire con lo stato interno dell'oggetto in modi non previsti.

La classe dovrebbe essere autonoma e inizializzarsi su un buono stato. Quello che sembra fare è giocare con i modelli solo per vedere cosa possono fare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top