2014-09-30 14 views
20

Vorrei copiare fino a N elementi.std :: copia n elementi o alla fine

template< class InputIt, class Size, class OutputIt> 
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result) 
{ 
    Size c = count; 
    while (first != last && c > 0) { 
     *result++ = *first++; 
     --c; 
    } 
    return result; 
} 

c'è un modo per farlo con le funzioni std? Ho potuto anche:

template< class InputIt, class Size, class OutputIt> 
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result) 
{ 
    if(std::distance(first, last) > count) 
     return std::copy_n(first,count,result); 
    return std::copy(first,last,result); 
} 

però, oltre ad essere ingombrante, va oltre la gamma due volte (distanza, copia). Se sto usando un iteratore di trasformazione o un filtro iteratore, quelle sono chiamate non necessarie (N) alla funzione filtro/trasformazione.

template <class InputIt, class OutputIt> 
OutputIt copy_n_max(InputIt begin, InputIt end, OutputIt last, size_t count) 
{ 
    return std::copy_if(begin, end, last, 
         [&count](typename std::iterator_traits<InputIt>::reference) 
         { return count--> 0; }); 
} 

int main() 
{ 
    std::vector<int> v({1,2,3,4,5,6,7,8,9}), out; 
    copy_n_max(v.begin(), v.end(), std::back_inserter(out), 40); 
    for(int i : out) std::cout <<i << " ,"; 
} 

uscite 1,2,3,4,5,6,7,8,9,

tuttavia, questo continuerà fino alla fine, e non contare volte. così ancora, più chiamate inutili al mio filtro funzione/trasformare ...

+1

Puoi essere più specifico su ciò che stai cercando di fare? –

+6

@remyabel Mi sembra abbastanza specifico. –

+1

Per essere onesti, la prima soluzione sembra la più semplice; chiamalo qualcosa come copy_upto_n() e chiamalo un giorno; Attraversa la gamma solo una volta (quantità minima di vincoli) e non fa mai più del conteggio delle dereferenze dell'input, a differenza della terza soluzione. Sono stato davvero sorpreso dal fatto che copy_n non si comporta in questo modo in un primo momento ... –

risposta

14

Se si ha accesso a tutta la struttura dei dati, e quindi la sua dimensione, è possibile utilizzare il seguente:

std::vector<int> v1, v2; 
std::copy_n(v2.begin(), std::min(NUM, v2.size()), std::back_inserter(v1)); 

Se si dispone di accesso solo agli iteratori, non so come farlo usando solo le funzioni std senza calcolare la distanza. Questo è economico per gli iteratori ad accesso casuale, ma i duplicati funzionano per altri tipi.

std::vector<int>::iterator i_begin, i_end, o_begin; 
std::copy_n(i_begin, std::min(NUM, std::distance(i_begin, i_end)), o_begin); 
+0

sì, la tua strada è meno ingombrante ma il tempo di esecuzione è lo stesso. Penso che questo sia il meglio che otterremo. dovrò copiare da solo o pagare il prezzo per "distanza" se la funzione filtro/trasformazione è trascurabile. sembra un peccato che copy_n sia protetto da un iteratore finale. – user2232888

+1

@ user2232888 La complessità di 'std :: distance' è costante per gli iteratori ad accesso casuale.Quindi "std :: distance" è gratuito, o conosci la dimensione del contenitore prima della mano e la prima soluzione di [Neil Kirk] (http://stackoverflow.com/users/2068573/neil-kirk) funzionerà. –

+1

Johathan Mee, non è corretto quando tutto ciò che hai sono iteratori. Calling ++ può essere costoso se si utilizzano filtri/trasformatori iteratori ... – user2232888

8

Vorrei andare per qualcosa di simile a questo:

template <class InputIt, class OutputIt> 
OutputIt copy_n_max(InputIt begin, InputIt end, OutputIt last, size_t count) 
{ 
    return std::copy_if(begin, 
         end, 
         last, 
         [&count](typename std::iterator_traits<InputIt>::reference) -> bool 
         { 
          if (count > 0) 
          { 
           --count; 
           return true; 
          } 
          return false; 
         }); 
} 

Utilizzando copy_if predicato per verificare se o non abbastanza di questo ingresso è stato copiato. Il vantaggio principale che vedo qui non è un ulteriore std::distance coinvolto.

Live example on IDEOne

+0

Sono abbastanza sicuro che il compilatore lo semplificherà. E lo trovo più leggibile. – Johan

+0

sembra funzionare. Mi piace questa soluzione – user2232888

+1

'decltype (* begin) &' => no, non proprio. Quello che vuoi è 'typename std :: iterator_traits :: reference', solo così che funzioni con * proxy * (come in' std :: vector '). In C++ 14, potresti anche usare 'auto &', e non guardare indietro. –

7

C'è un modo semplice per utilizzare il std::copy_if -overload aggiunto da C++ 11 per il vostro compito (ha bisogno solo InputIterators):

template< class InputIt, class Size, class OutputIt> 
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result) 
{ 
    return std::copy_if(first, last, result, 
     [&](typename std::iterator_traits<InputIt>::reference) 
     {return count && count--;}); 
} 

BTW: Ottiene ancora migliore in C + 14, senza bisogno di una variabile o un argomento così complesso:

std::copy_if(first, last, result, 
    [count = some_complicated_expression](auto&&) mutable 
    {return count && count--;}); 
+0

L'unico fastidio sono i controlli non necessari quando' count' è più grande della dimensione dell'intervallo. – jrok

+0

Beh, sì , si potrebbe aggiungere qualche modello con tutte le ottimizzazioni e le scorciatoie usate nella libreria standard.Penso che un sovraccarico per 'std :: copy_n' dovrebbe essere praticabile – Deduplicator

+0

@Deduplicator: Ah! Avevo completamente ignorato questo fatto! è 'copy_if' e non 'copy_until'. –

1

è possibile utilizzare copy_if con un predicatore personalizzato, e funziona per anziani ve rsioni di C++.

#include <algorithm> 
#include <iostream> 
#include <vector> 
#include <iterator> 


struct LimitTo 
{ 
    LimitTo(const int n_) : n(n_) 
    {} 

    template< typename T > 
    bool operator()(const T&) 
    { 
     return n-->0; 
    } 

    int n; 
}; 

int main() 
{ 
    std::vector<int> v1{ 1,2,3,4,5,6,7,8 }; 
    std::vector<int> v2; 

    std::copy_if(std::begin(v1), std::end(v1), std::back_inserter(v2), LimitTo(3)); 

    std::copy(std::begin(v1), std::end(v1), std::ostream_iterator<int>(std::cout,", ")); 
    std::cout << std::endl; 
    std::copy(std::begin(v2), std::end(v2), std::ostream_iterator<int>(std::cout,", ")); 
    std::cout << std::endl; 
} 

Questo esempio copia n elementi, utilizzando il predicatore LimitTo.