Funzione template: Passando agli iteratori

4

Ho difficoltà a fare una scelta di progettazione nella seguente configurazione:

Sto scrivendo (C ++) funzioni che prendono una coppia di iteratori (in contenitori di template) e calcolano un valore di ritorno dello stesso tipo a cui gli iteratori puntano.

Diciamo che voglio implementare una funzione somma (questo è solo un esempio per illustrare il punto).

Per come la vedo io ho due opzioni e entrambe hanno uno svantaggio:

Opzione 1

Rendi il parametro del template funzione T definendo il tipo che il contenitore memorizza.

Ho solo bisogno di un parametro, ma posso usare la funzione solo su iteratori di MyContainer .

template <class T>
T sum(typename MyContainer<T>::IteratorRange range){

    T sum;
    for (auto it = range.first; it < range.second; ++it) {
        sum += *it;
    }
    return sum;
}

Opzione 2

Chiedi alla funzione di assumere una classe di template IteratorRange arbitraria e una seconda classe U che determina il tipo di ritorno.

Mentre gli iteratori possono essere originati da qualsiasi contenitore, ho bisogno di due classi template anche se il tipo restituito è sempre lo stesso tipo puntato dagli iteratori.

template <class IteratorRange, class U>
U sum(IteratorRange range){

    U sum;
    for (auto it = range.first; it < range.second; ++it) {
        sum += *it;
    }
    return sum;
}

Quale opzione è più pulita o esiste un'alternativa che offra i vantaggi di entrambe le opzioni?

Domanda bonus

Come si dovrebbe inizializzare la variabile sum ?

    
posta 1v0 04.08.2015 - 16:25
fonte

1 risposta

14

L'intervallo per scorrere su

La libreria standard C ++ offre qualcosa di una forma canonica qui, accumulate ;

template< class InputIt, class T >
T accumulate( InputIt first, InputIt last, T init );

Prende un intervallo dal primo all'ultimo (escluso l'ultimo) e aggiunge ciascun elemento nell'intervallo al valore init come valore iniziale. Trarrà anche il tipo di ritorno dal tipo iniziale.

Se è richiesta un'implementazione di sum , consiglierei l'uso di accumulate qui così com'è. Se non è adatto, la firma della funzione che assume l'intervallo [primo, ultimo) è "normale" come rispecchia la libreria standard.

Deduce il tipo di reso

Per ottenere il tipo di valore "dietro" l'iteratore, std::iterator_traits , in particolare l'embedded digitare value_type può essere utilizzato;

typedef typename std::iterator_traits<InputIt>::value_type Result;
// .. default initialise
Result sum = Result{}; // or Result() if earlier than C++11

Scelta del design

Disegnerei la funzione su (variazione dell'opzione 2);

  • Accetta un intervallo [primo, ultimo)
  • Usa std::iterator_traits (che funziona anche con i puntatori) per dedurre il tipo di ritorno
  • Usa l'inizializzazione predefinita di C ++ 11 per inizializzare il risultato

Come segue:

template <class Iterator, class U = typename std::iterator_traits<Iterator>::value_type>
U sum(Iterator first, Iterator last)
{
    U sum = U{};
    for (auto it = first; it != last; ++it) {
        sum += *it;
    }
    return sum;
}

Un'alternativa con argomenti template ridotti.

template <class Iterator>
typename std::iterator_traits<Iterator>::value_type sum(Iterator first, Iterator last)
{
    using U = typename std::iterator_traits<Iterator>::value_type;
    U sum = U{};
    for (auto it = first; it != last; ++it) {
        sum += *it;
    }
    return sum;
}
    
risposta data 04.08.2015 - 16:30
fonte

Leggi altre domande sui tag