2016-05-15 15 views
8

Sono interessato all'implementazione di un ambiente simile alla raccolta Java per C++. So che questa non è una buona idea e così via, ma non voglio usarla più tardi, ma solo imparare come fare un OOP avanzato.Interfaccia con funzione membro modello

Il mio problema è che voglio un modello di classe base collection<T> con funzioni puramente virtuali. Una di queste funzioni deve essere map() che prende uno std::function<R(T)>. Dal momento che map() dovrebbe essere virtuale, non so quale tipo di reso dovrei usare per questo. collection<R> non è possibile perché i modelli di funzione membro non possono essere virtuali.

Come posso aggiungere tale funzione membro map() per l'interfaccia collection<T>?

+0

Cosa c'è che non va nei contenitori standard C++? –

+2

@ πάντα ῥεῖ OP sta cercando di fare questo per scopi di apprendimento credo. –

+0

niente voglio sapere come implementare una cosa del genere per scopi educativi – Exagon

risposta

7

Come posso aggiungere tale funzione membro map per la mia interfaccia collection<T>?

La risposta breve è: non è così. Se ho un po 'di collection<int> e voglio mapstd::to_string su di esso, ho bisogno di produrre un collection<std::string>. Ma un vector_collection<int> deve produrre un numero vector_collection<std::string> e un list_collection<int> deve produrre un list_collection<std::string> - in modo che la trasformazione del tipo debba essere virtual ized. Ma non puoi avere modelli di funzioni membro virtual, quindi non c'è modo di esprimerlo.

Per far sì che funzioni, è necessario disporre di un tipo di base comune per tutti gli oggetti che si stanno inserendo nel contenitore e quindi avere solo una facciata comune tra cui si può effettuare il cast. Cioè, in realtà hai solo collection<unique_ptr<Object>> dove map ti dà solo un diverso collection<unique_ptr<Object>>, e tu solo map il tuo collection_facade<int, collection<unique_ptr<Object>>> in un collection_facade<std::string, collection<unique_ptr<Object>>>. Con un sacco di lavoro e totale disprezzo per le prestazioni e la sicurezza del tipo, potresti arrivarci.


Questo è il vantaggio dei modelli. Se voglio scrivere map per qualcosa come vector, posso solo scrivere che:

template <class T, class A, class F, class R = std::result_of_t<F(T)>> 
std::vector<R, A> map(std::vector<T, A> const& v, F f) { 
    std::vector<R, A> mapped; 
    mapped.reserve(v.size()); 
    for (T const& elem : v) { 
     mapped.push_back(f(elem)); 
    } 
    return mapped; 
} 

o:

template <class T, class A, class F, class R = std::result_of_t<F(T)>> 
std::vector<R, A> map(std::vector<T, A> const& v, F f) { 
    return std::vector<R, A>(
     boost::make_transform_iterator(v.begin(), f), 
     boost::make_transform_iterator(v.end(), f) 
     ); 
} 

devo implementare map() per ogni contenitore a parte - ma avrei dovuto farlo Comunque. E ora non sto dando nulla. Inoltre, quanto spesso stai scrivendo algoritmi che sono runtime-container-agnostici?

+0

E anche l'allocatore può essere rimbalzato. – Jarod42

0

Implementare map come una funzione di modello esterno. Ad esempio, è possibile scomporre map in due fasi, produttore virtuale interno e consumatore con modello esterno.

template<typename T> struct Collection { 
    // virtual T next(); // Java way 
    // C++ way 
    // In simplest cases you can rely on iterator pairs. 
    struct const_iterator { 
     T const &operator*() const; 
     const_iterator &operator++(); 
    } 
    virtual const_iterator begin() const; 
    virtual const_iterator end() const; 
}; 
template<typename R, typename T> Collection<R> map(
    Collection<T> const &coll, std::function<R(T)> const &f); 

Per attuare contenitori essenzialmente complicati e composizioni monadici si può anche negare begin() e end() e scrivere un esplicito (parziale) modello specializzazione.

Problemi correlati