2016-05-15 4 views
8

Я заинтересован в реализации подобной Java-коллекции среды для C++. Я знаю, что это не очень хорошая идея и так далее, но я действительно не хочу использовать его позже, но просто научись делать некоторые продвинутые ООП.Интерфейс с функцией члена-шаблона

Моя проблема в том, что я хочу шаблон базового класса collection<T> с чисто виртуальными функциями. Одна из этих функций должна быть map(), которая принимает std::function<R(T)>. Поскольку map() должен быть виртуальным, я не знаю, какой тип возврата я должен использовать для него. collection<R> невозможно, поскольку шаблоны функций-членов не могут быть виртуальными.

Как добавить функцию map() для моего интерфейса collection<T>?

+0

Что случилось с стандартными контейнерами C++? –

+2

@ πάντα ῥεῖ ОП пытается сделать это для учебных целей, я думаю. –

+0

ничего не хочу знать, как реализовать такую ​​вещь для образовательных целей – Exagon

ответ

7

Как добавить функцию map для моего интерфейса collection<T>?

Короткий ответ: вы этого не делаете. Если у меня есть collection<int>, и я хочу mapstd::to_string на нем, мне нужно создать collection<std::string>. Но vector_collection<int> необходимо произвести vector_collection<std::string>, а list_collection<int> необходимо создать list_collection<std::string> - так что преобразование типа должно быть virtual ized. Но у вас не может быть virtual шаблонов функций-членов, поэтому нет способа выразить это.

Для того, чтобы это сработало, вам необходимо иметь общий базовый тип для всех объектов, которые вы помещаете в свой контейнер, а затем просто иметь общий фасад, который вы можете использовать между ними. То есть у вас действительно есть только collection<unique_ptr<Object>>, где map просто дает вам другой collection<unique_ptr<Object>>, а вы только mapcollection_facade<int, collection<unique_ptr<Object>>> в collection_facade<std::string, collection<unique_ptr<Object>>>. С большой работой и полным пренебрежением к производительности и типу безопасности вы можете добраться туда.


Это преимущество шаблонов. Если я хочу написать map что-то вроде vector, я могу просто написать, что:

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

или:

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

Я должен реализовать map() для каждого контейнера отдельно - но я бы это сделать так или иначе. И теперь я ничего не даю. Кроме того, как часто вы пишете алгоритмы, которые являются агентами runtime-container-agnostic?

+0

И «распределитель» также может быть отскок. – Jarod42

0

Внесите map в качестве внешней функции шаблона. Например, вы можете разложить map в два этапа, внутренний виртуальный производитель и внешний шаблонный потребитель.

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

Для реализации существенно сложных контейнеров и монадическая композиции вы можете даже отрицать begin() и end() и написать явную (частично) специализацию шаблона.

Смежные вопросы