Search code examples
c++c++11abstract-class

How to declare operator++(int) in an abstract class?


I have an abstract class that is basically meant to serve as an "iterator interface". That is, it is an abstract iterator that a few concrete classes will later implement. To make the abstract class an iterator, I need to overload T operator++(int), where T is the class for which the operator is overloaded.

class AbstractClass {
    virtual AbstractClass operator++(int) = 0; // compile time error
    virtual AbstractClass& operator++() = 0;   // but this is fine
}

However, I cannot write AbstractClass operator++(int) as only pointers and references to an abstract class are permitted as return values.

Is there a way to require the subclasses to overload operator++(int) in plain C++11?


Solution

  • Abstract classes with virtual members are usually accessed through base pointers, so you teh caller doesn't know the derived type, but the return value from operator++(int) must be known at compile time by the caller, so there's no way to do this directly. I would simply not provide this method, and have your class be iterator-like, but not fully iterator-conforming.

    However, there is a complex workaround, and that's to make a non-abstract iterator that itself can handle a virtual iterator as a member. This gets super complicated.

    //abstract interface for iterators
    template<class value_type>
    struct virtual_iterator_interface {
        virtual ~virtual_iterator_interface(){};
        virtual std::unique_ptr<virtual_iterator_interface> clone()const=0;
        virtual value_type& deref()=0;
        virtual void increment()=0;
    };
    //wrapper implementation for iterators
    template<class value_type, class It>
    struct virtual_iterator : virtual_iterator_interface<value_type> {
        It it;
        virtual_iterator(It it_) : it(it_) {}
        std::unique_ptr<virtual_iterator_interface> clone()const
        {return std::make_unique<virtual_iterator>(it);}
        value_type& deref()
        {return *it;}
        void increment()
        {return ++it;}
    };
    
    static const struct from_iterator_t {} from_iterator;
    // The iterator that holds a pointer to an abstracted iterator
    template<class value_type>
    class erased_iterator {
        std::unique_ptr<virtual_iterator_interface<value_type>> ptr;
    public:
        template<class It>
        erased_iterator(from_iterator_t, It it)
        :ptr(std::make_unique<virtual_iterator<value_type,It>>(it)) {}
        erased_iterator(std::unique_ptr<virtual_iterator_interface<value_type>> ptr_)
        :ptr(std::move(ptr_)) {}
        erased_iterator(const erased_iterator& rhs)
        :ptr(rhs.ptr->clone()) {}
        erased_iterator(erased_iterator&& rhs) = default;
        erased_iterator& operator=(const erased_iterator& rhs)
        {ptr=rhs.ptr->clone();}
        erased_iterator& operator=(erased_iterator&& rhs) = default;
    
        //TADA! Iterator things are now possible!
        value_type& operator*(){return ptr->deref();}
        erased_iterator& operator++(){ptr->increment(); return *this;}
        erased_iterator operator++(){erased_iterator t(it->clone()); ptr->increment(); return t;}
    };
    

    In addition to complexity, this allocates iterators on the heap, which makes them very slow.

    Also note: virtual members prevent inlining and optimization, so makes the code slower. For things like iterators, which are supposed to be lightweight, this can actually make the code a lot slower.