2016-03-08 4 views
0

Скажем, у меня есть виртуальный базовый класс Base, который частично будет вести себя как контейнер с двумя производными классами VectorLike и RangeLike. Я хочу, чтобы достичь чего-то вроде следующего:Возвращение различных итераторов с виртуальными производными методами

class VectorLike : public Base { 
    std::vector<int> data; 
public: 
    virtual std::vector<int>::const_iterator cbegin() { return data.cbegin() } 
    virtual std::vector<int>::const_iterator cend() { return data.cend() } 
} 

class RangeLike : public Base { 
    int min, max; 
    class const_iterator { 
     int x; 
    public: 
     int operator++() { return ++x } 
     bool operator==(const_iterator rhs) { return x == rhs.x } 
     const_iterator(int y) { x = y } 
    } 
public: 
    virtual const_iterator cbegin() { return const_iterator(min); } 
    virtual const_iterator cend() { return const_iterator(max); } 
} 

Этот код не будет компилироваться, так как std::vector<int>::const_iterator и RangeLike::const_iterator не являются идентичными или covariant. Чтобы достичь второго, мне нужен базовый класс итератора, из которого будут получены и std::vector<int>::const_iterator, и RangeLike::const_iterator. Но тогда еще cbegin() и cend() должны будут возвращать указатели на итераторы, которые сделают еще больший беспорядок.

Мой вопрос в том, можно ли достичь чего-то подобного вышеуказанному коду, и если да, то как?

+0

Возможный дубликат (http://stackoverflow.com/questions/4222279/it -is-possible-to-change-return-type-when-override-a-virtual-function-in-c) –

+0

Вам понадобится ваш собственный полиморфный тип итератора, который обертывает векторный итератор и итератор диапазона –

+0

В этом случае производные классы должны быть известны заранее. Если я решит ввести третий производный класс, я должен отредактировать базовый итератор. Это, и я в основном храню двойные итераторы, которые я хочу хранить, что вряд ли кажется оптимальным. – MadPidgeon

ответ

1

Вот реализация полиморфного константного итератора const. Вы можете построить его с любым типом итератора (включая указатели), где std::iterator_traits<Iter>::value_type разрешается до int.

Это должно быть как для std::vector<int>, так и для your_range_type<int>.

Это должно вас начать.

#include <iostream> 
#include <vector> 
#include <array> 
#include <memory> 
#include <algorithm> 

struct poly_const_iterator 
{ 
    using value_type = int; 

    struct concept { 
     virtual void next(int n) = 0; 
     virtual const value_type& deref() const = 0; 
     virtual bool equal(const void* other) const = 0; 
     virtual std::unique_ptr<concept> clone() const = 0; 
     virtual const std::type_info& type() const = 0; 
     virtual const void* address() const = 0; 
     virtual ~concept() = default; 
    }; 

    template<class Iter> 
    struct model : concept 
    { 
     model(Iter iter) : _iter(iter) {} 

     void next(int n) override { _iter = std::next(_iter, n); } 
     const value_type& deref() const override { return *_iter; } 
     bool equal(const void* rp) const override { return _iter == static_cast<const model*>(rp)->_iter; } 
     std::unique_ptr<concept> clone() const override { return std::make_unique<model>(*this); } 
     const std::type_info& type() const override { return typeid(_iter); } 
     const void* address() const override { return this; } 


     Iter _iter; 
    }; 

    std::unique_ptr<concept> _impl; 

public: 
    // interface 

    // todo: constrain Iter to be something that iterates value_type 
    template<class Iter> 
    poly_const_iterator(Iter iter) : _impl(std::make_unique<model<Iter>>(iter)) {}; 

    poly_const_iterator(const poly_const_iterator& r) : _impl(r._impl->clone()) {}; 

    const value_type& operator*() const { 
     return _impl->deref(); 
    } 

    poly_const_iterator& operator++() { 
     _impl->next(1); 
     return *this; 
    } 

    bool operator==(const poly_const_iterator& r) const { 
     return _impl->type() == r._impl->type() 
     and _impl->equal(r._impl->address()); 
    } 

    bool operator != (const poly_const_iterator& r) const { 
     return not(*this == r); 
    } 
}; 

void emit(poly_const_iterator from, poly_const_iterator to) 
{ 
    std::copy(from, to, std::ostream_iterator<int>(std::cout, ", ")); 
    std::cout << std::endl; 
} 

int main() 
{ 
    std::vector<int> v = { 1, 2, 3, 4, 5 }; 
    std::array<int, 5> a = { 6, 7,8, 9, 0 }; 

    emit(std::begin(v), std::end(v)); 
    emit(std::begin(a), std::end(a)); 


    return 0; 
} 

Ожидаемые результаты: [? Можно изменить тип возвращаемого значения, если переопределить виртуальную функцию в C++]

1, 2, 3, 4, 5, 
6, 7, 8, 9, 0, 
+0

Это замечательно! Вы принудительно наследуете класс шаблона, а затем аккуратно обертываете класс итератора вокруг указателя. Использование в emit выглядит действительно чисто. – MadPidgeon

+0

не забудьте добавить операторы перемещения, назначения и перемещения. жаль, что я забыл об этом. –

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