2016-05-15 23 views
8

Jestem zainteresowany implementacją środowiska Java-kolekcji podobnego do C++. Wiem, że to nie jest dobry pomysł i tak dalej, ale tak naprawdę nie chcę z niego korzystać później, ale po prostu naucz się robić zaawansowaną technikę OOP.Interfejs z funkcją elementu szablonu

Mój problem polega na tym, że chcę mieć szablon klasy podstawowej collection<T> z czysto wirtualnymi funkcjami. Jedną z tych funkcji powinien być map(), który zajmuje std::function<R(T)>. Ponieważ map() powinien być wirtualny, nie wiem, który typ zwracania powinienem dla niego użyć. collection<R> nie jest możliwe, ponieważ szablony funkcji członków nie mogą być wirtualne.

Jak mogę dodać taką funkcję składową map() do mojego interfejsu collection<T>?

+0

Co jest nie tak z kontenerami standardowymi C++? –

+2

@ πάντα ῥεῖ OP próbuje to zrobić dla celów edukacyjnych. –

+0

nic Po prostu chcę wiedzieć, jak zaimplementować coś takiego dla celów edukacyjnych. – Exagon

Odpowiedz

7

Jak mogę dodać taką funkcję dla mojego interfejsu dla mojego interfejsu collection<T>?

Krótka odpowiedź brzmi: nie. Jeśli mam jakieś collection<int> i chcę mapstd::to_string na to, muszę wyprodukować collection<std::string>. Ale vector_collection<int> musi wyprodukować vector_collection<std::string> i list_collection<int> musi wyprodukować list_collection<std::string> - tak, że sama transformacja typu musi być virtual. Ale nie możesz mieć szablonów funkcji członka, więc nie ma sposobu, aby to wyrazić.

Aby to zadziałało, musiałbyś mieć wspólny podstawowy typ dla wszystkich obiektów, które wkładasz do swojego kontenera, a następnie mieć po prostu wspólną fasadę, którą możesz rzucać. To znaczy, że masz tylko collection<unique_ptr<Object>>, gdzie map daje tylko collection<unique_ptr<Object>> i tylko map swój collection_facade<int, collection<unique_ptr<Object>>> do collection_facade<std::string, collection<unique_ptr<Object>>>. Z dużą ilością pracy i całkowitym lekceważeniem wydajności i bezpieczeństwa typu można się tam dostać.


Jest to zaleta szablonów. Jeśli chcę napisać map czegoś podobnego vector, mogę tylko napisać, że:

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; 
} 

czyli

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) 
     ); 
} 

muszę wdrożyć map() dla każdego pojemnika oddzielnie - ale będę musiał to zrobić tak czy inaczej. A teraz nic nie daję. Poza tym, jak często piszesz algorytmy, które są agnostyczne w trybie runtime-container?

+0

I 'allocator' może również odbijać. – Jarod42

0

Implementacja map jako zewnętrznej funkcji szablonu. Na przykład można rozłożyć map w dwóch etapach, wewnętrzny wirtualny producent i zewnętrzny szablon konsumenta.

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); 

celu realizacji zasadniczo skomplikowane pojemniki i monadycznego kompozycje można nawet zaprzeczyć begin() i end() i napisać wyraźny (częściowe) specjalizacji szablonu.