2016-04-29 7 views
5

La seguente funzione applica un funtore su ogni elemento e riduce il valore di ritorno:modello Iterator-riduttore per le funzioni che possono o non possono restituire un valore

template <class FCT, class RED> 
RED::TYPE forAllElements(FCT functor, RED reducer){ 
    for(/* all elem in elements */){ 
    reducer(functor(elem)); 
    } 
    return reducer.value; 
} 

Ora, a volte potrei desiderare di chiamare solo il functor su tutti gli elementi, e non ridurre nulla. In sostanza, Vorrei poi avere qualcosa di simile:

class FunctorThatReturnsNothing{ 
    void operator() (Elem e){ 
    // do something, return nothing... 
    } 
} 

class DummyReducer{ 
    using TYPE = void; // ??? something like that ??? 

    template <class FCT> 
    void operator() (/* ??? what here */){ 
    // do nothing... 
    } 
} 

forAllElements(FunctorThatReturnsNothing(), DummyReducer()); 

Ma questo non verrà compilato dal momento che ho reducer(functor(elem)) in cui il valore di ritorno inesistente di una funzione void è preso come argomento.

C'è un modo per farlo funzionare per i void functors senza duplicare forAllElements per un void e un caso diverso da void?

(Per le persone che sospettano un XY-Problema:. Io fondamentalmente so approcci diversi per l'iterazione e la riduzione e penso che l'approccio callback presentato è appropriato per il mio caso mi chiedo come posso evitare di avere il codice duplicato per il "valore di ritorno + riduzione" e il "solo callback" caso.)

+0

Quindi, in pratica si vuole [ 'std :: for_each'] (http://en.cppreference.com/w/ cpp/algoritmo/for_each)? –

+0

@JoachimPileborg, vedere l'ultima parte della domanda tra parentesi. So che potrei seguire questa strada con gli iteratori, ma nella situazione attuale, una soluzione di callback come presentata è favorevole. – Michael

+1

L'approccio più pulito sarebbe "restituire riduttore" anziché "restituire reducer.value". Puoi refactoring il tuo codice per usarlo? – filipos

risposta

1

Penso che si può solo fare una classe VoidReducer, ma invece di return reducer.value; si avrebbe bisogno return reducer.getvalue();. Quindi fai semplicemente void VoidReducer::getvalue(){}.

Non l'ho provato ma l'idea dovrebbe funzionare. È consentito a return f(); se entrambi f e la funzione corrente hanno restituito il tipo void.

EDIT
Ora che ho letto la domanda con più attenzione, vedo che il problema si sta chiedendo è la linea reducer(functor(elem));.
Per questo vorrei spedire in tempo di compilazione basato su decltype(functor(elem)).

template <class Functor, class Reducer, class Elem> 
void Combine(Functor functor, Reducer & reducer, Elem elem, std::true_type) 
{ 
    functor(elem); 
} 

template <class Functor, class Reducer, class Elem> 
void Combine(Functor functor, Reducer & reducer, Elem elem, std::false_type) 
{ 
    reducer(functor(elem)); 
} 

template <class Functor, class Reducer, class Elem> 
void Combine(Functor functor, Reducer & reducer, Elem elem) 
{ 
    Combine(functor, reducer, elem, std::is_same<decltype(functor(elem)), void>()); 
} 

quindi chiamando Combine invece di reducer(functor(elem)) sarà correttamente ridurre il valore di ritorno di functor se e solo se non è vuoto.

PS: Sprinkle reference e std::forward chiamate a piacere.

1

Se si desidera continuare a utilizzare la funzione forAllElements invece se si utilizza std::for_each direttamente è possibile creare un sovraccarico della funzione che non prende un riduttore, e semplicemente utilizza std::for_each internamente:

template <class FCT> 
void forAllElements(FCT functor){ 
    std::for_each(std::begin(...), std::end(...), functor); 
} 

... 

forAllElements(FunctorThatReturnsNothing()); 

Se non è è possibile ottenere "iteratori" per il contenitore (se si utilizza un contenitore o puntatori non standard), è possibile avere il proprio loop e semplicemente chiamare functor.

0

Un approccio pulito e flessibile consiste nel restituire l'oggetto funzione stesso anziché un campo codificato value.

template <class FCT, class RED> 
RED forAllElements(FCT functor, RED reducer){ 
    for(/* all elem in elements */){ 
    reducer(functor(elem)); 
    } 
    return reducer; 
} 

L'operazione può quindi non avere alcun valore di ritorno, o anche più di un valore di ritorno, con nomi informativi.

Il vostro metodo originale può essere visto come un wrapper convenienza su questo componente primitiva:

template <class FCT, class RED> 
RED::TYPE forAllElements_convenience(FCT functor, RED reducer){ 
    return forAllElements(functor, reducer).value; 
} 
Problemi correlati