2015-12-17 7 views
5

Si consideri il seguente scenario:Utilizzando negazione della UnaryPredicate in erase-remove idioma

bool is_odd(int i) 
{ 
    return (i % 2) != 0; 
} 
int main() 
{ 
    // ignore the method of vector initialization below. 
    // assume C++11 is not to be used. 
    std::vector<int> v1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 
    std::vector<int> v2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 

    // removes all odd numbers, OK 
    v1.erase(std::remove_if(v1.begin(), v1.end(), is_odd), v1.end()); 

    // remove all even numbers 
    v2.erase(std::remove_if(v2.begin(), v2.end(), ???), v2.end()); 
} 

Posso usare lo stesso is_odd() UnaryPredicate per rimuovere i numeri come previsto nell'ultima riga della main(). Oppure dovrò dare necessariamente una is_even() anche se non sarà altro che:

bool is_even(int i) 
{ 
    !is_odd(i); 
} 
+1

Se si dispone di C++ 11, un lambda è più semplice: '[] (int i) {return is_odd (i); } ', e direi più chiaro di qualcosa come' not1'. – BoBTFish

+0

scusa ho notato il = prima delle parentesi graffe –

+0

@HumamHelfawi: anche con '=' il codice precedente richiede C++ 11! Non esiste un modo diretto per inizializzare un 'std :: vector' (o qualsiasi altro contenitore di libreria standard) con una sequenza di elementi in C++ prima di C++ 11. Il modo per farlo sarebbe la creazione di un array e quindi l'uso degli iteratori nell'array con il costrutto del contenitore. –

risposta

9

controllo della funzione std::not1. fa quello che vuoi

v2.erase(std::remove_if(v2.begin(), v2.end(), std::not1(std::ptr_fun(is_odd))), v2.end()); 

Live example

In ogni caso, se è da me plus C++ 11 è disponibile io preferisco:

v2.erase(std::remove_if(v2.begin(), v2.end(), [&](auto/* or the type */ const& item){return !is_odd(item);}), v2.end()); 

perché per quanto mi ricordo std::not1 era disponibile prima lambda era a disposizione.

5

È possibile utilizzare std::not1. Purtroppo, std::not1 richiede un argomento oggetto funzione con i tipi nidificati argument_type e result_type. Cioè, non può essere usato direttamente. Invece, è necessario combinare l'uso con std::ptr_fun quando si utilizza il negatore con una funzione normale:

v2.erase(std::remove_if(v2.begin(), v2.end(), std::not1(std::ptr_fun(is_odd))), v2.end()); 

Alla riunione dell'ultimo Comitato std::not_fn è stata spostata dalla Library Fundamentals TS 2 nella bozza di lavoro. Cioè, c'è speranza che con C++ 17 ci sia una migliore offerta per un negatore generico.

In generale, il divertimento si interrompe quando è necessario utilizzare una qualsiasi delle funzioni std::*_fun. Come altri hanno sottolineato, può essere ragionevole usare un lambda invece:

v2.erase(std::remove_if(v2.begin(), v2.end(), [](auto&& x){ return !::is_odd(x); }), v2.end()); 

L'utilizzo di una funzione lambda o un oggetto funzione con un operatore del call inline funzione ha anche il vantaggio che il compilatore ha un tempo più facile inlining il codice.

Ovviamente, se è necessario utilizzare C++ prima di C++ 11 l'approccio std::not1/std::ptr_fun è il più semplice da utilizzare immediatamente, l'uso di una funzione lambda non è nemmeno possibile. In questo caso si consiglia di creare un oggetto semplice funzione di sostenere inlining:

struct not_odd { 
    template <typename T> 
    bool operator()(T const& value) const { return !::odd(value); } 
}; 
// ... 
v2.erase(std::remove_if(v2.begin(), v2.end(), not_odd()), v2.end()); 
+0

"il divertimento si interrompe quando devi usare uno qualsiasi le funzioni std :: * _ fun ". È solo divertente, o c'è qualche svantaggio tecnico/pratico? – CinCout

+0

@HappyCoder: il commento è un po 'ironico ma è basato sull'esperienza che la necessità di usare 'std :: ptr_fun' è facilmente dimenticata (come l'altra risposta dimostrata) e, più ancora, che l'uso del corretto la versione della versione 'std :: mem * _fun' con il puntatore funzione membro non è affatto ovvia. 'std :: mem_fn' risolve il problema abbastanza bene per i puntatori di funzioni dei membri. –