I have a question about implementing a shared iterator interface.
As common practice for postix operator the function might look like this:
IteratorClass operator ++(int) {
IteratorClass temp = *this;
//increment stuff
return temp
}
And most of the time this is fine. In my case I am trying to implement 3 iterators for one class. Each iterator will load a local collection class with data but each derived iterator would load it in a different way. Because the collection class would be the same and all of the code for the operators( postfix/prefix ++/--, *) would be the same I thought a nice way to implement this would be inheritance:
struct iterator {
protected:
Collection collection;
public:
operator++(int);
operator++;
operator--(int);
operator--;
virtual load() = 0;
}
struct iterator1 : public iterator {
virtual load() { custom load function }
}
struct iterator2 : public iterator {
virtual load() { custom load function }
}
The problem are the postfix operators... They are trying to create an object of an abstract type and then returning it. Any suggestions for workarounds or structure changes?
A class with one (or more) virtual pure functions is abstract, and it can't be used to create a new object, so it doesn't have a constructor.
An abstract class is a class that is designed to be specifically used as a base class. An abstract class contains at least one pure virtual function. You declare a pure virtual function by using a pure specifier ( = 0 ) in the declaration of a virtual member function in the class declaration.
1 Abstract Base Classes. An ABC is a class that contains one or more pure virtual member functions. Such a class is not concrete and cannot be instantiated using the new operator. Instead, it is used as a base class where derived classes provide the implementations of the pure virtual methods.
The int parameter is a dummy parameter used to differentiate between prefix and postfix versions of the operators. When the user-defined postfix operator is called, the value passed in that parameter is always zero, although it may be changed by calling the operator using function call notation (e.g., a.
Use the CRTP idiom to make the base class aware of the final class. For example:
template<typename T>
struct iterator_base {
public:
T operator++(int) {
T temp = static_cast<T&>(*this);
++*this;
return temp;
}
T& operator++() {
// ++ mutation goes here
return *this;
}
// ... likewise for --, etc.
};
struct iterator1: public iterator_base<iterator1> {
// ... custom load function
};
This approach is called static polymorphism, and allows you (in some cases) to completely eschew virtual
and therefore make your objects smaller. You can omit the load
declaration from the base classes and call T::load
as static_cast<T&>(*this).load()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With