C ++ 11 Funzioni membro Lambda vs Helper

6

Ci sono un sacco di metodi in una classe che voglio ripulire. Questi creano semplicemente una struttura di dati (con valori diversi) più e più volte e li aggiungono a un contenitore passato, in questo modo:

SomeClass::Foo(ContainerType& cont)
{
   AnotherType dataItem;
   dataItem.item1 = true;
   dataItem.item2 = 42;
   // blah blah
   cont.addItem(dataItem);

   // ... snip ...

   AnotherType newItem;
   newItem.item1 = false;
   newItem.item2 = 128;
   // blah blah
   cont.addItem(newItem);
}

È piuttosto conveniente (codice rilevante all'interno della stessa funzione, così facile da ottenere e leggere) per avere una funzione lambda locale per ogni metodo e chiamarla invece (le modifiche future saranno limitate a un posto), in questo modo:

SomeClass::Foo(ContainerType& cont)
{
   auto buildData = [](ContainerType& cont, bool flag, int value){
       AnotherType dataItem;
       dataItem.item1 = true;
       dataItem.item2 = 42;
       // blah blah
       cont.addItem(dataItem);
   };
   buildData(cont, true, 42);
   // ... snip ...
   buildData(cont, false, 128);
}

anziché aggiungere una nuova funzione membro privata per fare lo stesso:

SomeClass::Foo(ContainerType& cont)
{
    buildData(cont, true, 42);
    buildData(cont, false, 128);
}
SomeClass::buildData(ContainerType& cont, bool flag, int val)
{
   // blah blah
}

Dato che gli interni di come creare% oggetti% co_de non sono utili / necessari al di fuori del metodo AnotherType , ecco le domande a cui chiedo assistenza:

  • Ci sono altri detergenti / soluzioni migliori a questo?
  • Ci sono delle insidie non ovvie nell'usare l'approccio lambda locale rispetto al nuovo approccio dei membri privati?

Al momento, ho utilizzato l'approccio locale di lambda e ho tagliato un sacco di codice ripetuto. Voglio solo assicurarmi che questa sia una strada accettabile.

    
posta Bhargav Bhat 13.08.2015 - 12:04
fonte

1 risposta

5

Entrambe le soluzioni hanno senso e hanno diversi compromessi.

lambda

Lo stile dell'uso di lambda in un'altra funzione è molto comune in molte lingue. Se ti sembra estraneo, è solo perché Lambdas non era disponibile in C ++ fino a poco tempo fa. Il vantaggio di questo stile è che l'intera implementazione della funzione è all'interno di quella funzione, con il raggruppamento lambda comportamento ripetuto in un singolo blocco. Dato che lambda può catturare variabili locali ( cont nel tuo caso), in genere non devi passare molti argomenti. Gli svantaggi sono che questa funzione può ancora crescere piuttosto a lungo e che non è possibile testare in modo indipendente il comportamento estratto.

Funzioni con nome

Se si utilizza una funzione appropriata (una funzione libera in uno spazio dei nomi anonimo o una funzione membro privata), la funzione principale diventa meno ingombrante e il comportamento estratto è verificabile in modo indipendente. Questo refactoring della funzione di estrazione è incredibilmente comune e rende più semplice testare e comprendere il codice: ogni funzione è lunga solo un paio di righe. E con nomi propri, tale codice diventa altamente auto-documentante.

Tuttavia, alcune parti del comportamento sono ora disseminate ovunque, e una completa comprensione del comportamento richiede che un lettore cucisca insieme tutti questi bit e pezzi: troppa astrazione può comportare una perdita di manutenibilità. Inoltre, il codice precedentemente interno può essere ora chiamato da qualsiasi altra funzione nel suo ambito, il che significa che le invarianti che sono ovvie nell'uso locale non sono ora garantite e devono essere ricontrollate se sei interessato al codice corretto, ad es. affermando che un puntatore non è nullo. Infine, uno svantaggio importante è che qualsiasi contesto su cui gli operatori della funzione devono essere passati esplicitamente come argomento. Questo può diventare noioso per 5-8 argomenti, dal momento che C ++ non ha i parametri formali con nome: (

For-loop

Un'altra possibilità, più come creare un lambda, è memorizzare i dati su cui stai operando in un vettore, e quindi eseguire un'azione su ogni oggetto tramite un ciclo. Per esempio:.

std::vector<WorkItem> items {
  {true, 42},
  {false, 128},
};
for (const auto& item : items) {
  ...
}

Spesso, il tipo di oggetto di lavoro potrebbe essere solo una tupla o una coppia. Non mi piace molto questa soluzione poiché l'ordinamento del codice è un po 'non intuitivo, ma è un metodo efficace per ottenere codice ripetuto.

Opinione personale

Quando estrao il comportamento in una funzione separata, inizialmente tendo ad usare un lambda poiché il vantaggio di catturare le variabili locali è enorme. Tuttavia, in successivi refactoring, tendo ad usare le funzioni nominate in modo da poterle testare più facilmente. Per me, questo supera il costo di perdere il contesto.

    
risposta data 13.08.2015 - 13:07
fonte

Leggi altre domande sui tag