2015-08-22 9 views
7

DatoSpostare tutti gli elementi che soddisfano alcune condizioni da un contenitore ad un altro, vale a dire che sto cercando una sorta di "move_if"

std::vector<T> first = /* some given data */, second; 

voglio mossa tutti gli elementi e che soddisfano una certa condizione cond(e)first-second, vale a dire qualcosa come

move_if(std::make_move_iterator(first.begin()), 
    std::make_move_iterator(first.end()), 
    std::back_inserter(second), [&](T const& e) 
{ 
     return cond(e); 
}); 

non era in grado di stabilire questo con la libreria algoritmi. Quindi, come posso farlo?

+0

Come su [ 'std :: copy_if'] (http://en.cppreference.com/w/cpp/algorithm/ copia) seguito da ['std :: remove_if'] (http://en.cppreference.com/w/cpp/algorithm/remove)? –

+0

@JoachimPileborg Perché pensi che 'remove_copy_if' faccia il lavoro? – 0xbadf00d

+5

'copy_if' con' move_iterator'. Assicurati che il tuo predicato stia prendendo la sua argomentazione da const ref. –

risposta

22

Se il mosso-da elementi in grado di rimanere dove sono in first, poi basta usare copy_if con move_iterator.

std::copy_if(std::make_move_iterator(first.begin()), 
      std::make_move_iterator(first.end()), 
      std::back_inserter(second), cond); 

Se il mosso-da elementi devono essere cancellate dal first, farei

// partition: all elements that should not be moved come before 
// (note that the lambda negates cond) all elements that should be moved. 
// stable_partition maintains relative order in each group 
auto p = std::stable_partition(first.begin(), first.end(), 
           [&](const auto& x) { return !cond(x); }); 
// range insert with move 
second.insert(second.end(), std::make_move_iterator(p), 
          std::make_move_iterator(first.end())); 
// erase the moved-from elements. 
first.erase(p, first.end()); 

O partition_copy con un move_iterator, seguito da assegnazione:

std::vector<T> new_first; 
std::partition_copy(std::make_move_iterator(first.begin()), 
        std::make_move_iterator(first.end()), 
        std::back_inserter(second), std::back_inserter(new_first), cond); 
first = std::move(new_first); 
1

Il motivo per cui move_if non esiste è perché ingigantirebbe la libreria. Utilizzare copy_if con move iterator o scriverlo manualmente.

copy_if(move_iterator<I>(f), move_iterator<I>(l), out); 

Ecco un'implementazione da Jonas_No trovato alla Channel9.

template <typename FwdIt, typename Container, typename Predicate> 
inline FwdIt move_if(FwdIt first, FwdIt last, Container &cont, Predicate pred) 
{ 
    if (first == last) 
     return last; // Empty so nothing to move 
    const size_t size = count_if(first, last, pred); 
    if (size == 0) 
     return last; // Nothing to move 
    cont.resize(size); 
    FwdIt new_end = first; 
    auto c = cont.begin(); 
    for (auto i = first; i != last; ++i) 
    { 
     if (pred(*i)) // Should it move it ? 
      *c++ = move(*i); 
     else 
      *new_end++ = move(*i); 
    } 
    return new_end; 
} 
1

@ T.C. ha fornito una soluzione perfettamente funzionante. Tuttavia, a prima vista, non si può capire quale sia l'intento di quel codice. Quindi, potrebbe non essere perfetto, ma io tendo a preferire qualcosa di simile:

template<class InputIt, class OutputIt, class InputContainer, class UnaryPredicate> 
OutputIt move_and_erase_if(InputIt first, InputIt last, InputContainer& c, OutputIt d_first, UnaryPredicate pred) 
{ 
    auto dist = std::distance(first, last); 
    while (first != last) 
    { 
     if (pred(*first)) 
     { 
      *d_first++ = std::move(*first); 
      first = c.erase(first); 
      last = std::next(first, --dist); 
     } 
     else 
     { 
      ++first; 
      --dist; 
     } 
    } 
    return d_first; 
} 
+1

chiamata iterativamente 'c.erase (first);' non sembra l'idea migliore per i contenitori che memorizzano i loro elementi in una memoria continua –

+0

e al primo sguardo 'std :: next (first, --dist); come un bug, perché stai settando 'last' per posizionare' current + (dimensione del contenitore precedente) -1' –

Problemi correlati