You can implement this by delegating the implementation of the iterator methods to virtual methods on the base class:
#include <cstdint>
#include <vector>
#include <list>
#include <variant>
#include <iostream>
struct Bin
{
int value;
};
class Store {
public:
virtual ~Store() = default;
// Iterators
class const_iterator;
virtual const_iterator begin() const = 0;
virtual const_iterator end() const = 0;
protected:
virtual void increment(const_iterator&) const = 0;
virtual const Bin& dereference(const_iterator&) const = 0;
};
class VectorStore : public Store {
public:
VectorStore()
:vector{{1},{2},{3},{4}}
{
}
const_iterator begin() const override;
const_iterator end() const override;
using const_iterator_state = std::vector<Bin>::const_iterator;
protected:
void increment(const_iterator&) const override;
const Bin& dereference(const_iterator&) const override;
private:
std::vector<Bin> vector;
};
class ListStore : public Store {
public:
ListStore()
:list{{2},{4},{6},{8}}
{
}
const_iterator begin() const override;
const_iterator end() const override;
using const_iterator_state = std::list<Bin>::const_iterator;
protected:
void increment(const_iterator&) const override;
const Bin& dereference(const_iterator&) const override;
private:
std::list<Bin> list;
};
class Store::const_iterator
{
public:
bool operator == (const Store::const_iterator& other)
{
return store == other.store && state == other.state;
}
bool operator != (const Store::const_iterator& other)
{
return !(*this == other);
}
Store::const_iterator& operator++()
{
store->increment(*this);
return *this;
}
const Bin& operator*()
{
return store->dereference(*this);
}
const Store* store;
std::variant<VectorStore::const_iterator_state, ListStore::const_iterator_state> state;
};
Store::const_iterator VectorStore::begin() const {
return {this, vector.begin()};
}
Store::const_iterator VectorStore::end() const {
return {this, vector.end()};
}
void VectorStore::increment(const_iterator& it) const
{
++std::get<VectorStore::const_iterator_state>(it.state);
}
const Bin& VectorStore::dereference(const_iterator& it) const
{
return *std::get<VectorStore::const_iterator_state>(it.state);
}
Store::const_iterator ListStore::begin() const {
return {this, list.begin()};
}
Store::const_iterator ListStore::end() const {
return {this, list.end()};
}
void ListStore::increment(const_iterator& it) const
{
++std::get<ListStore::const_iterator_state>(it.state);
}
const Bin& ListStore::dereference(const_iterator& it) const
{
return *std::get<ListStore::const_iterator_state>(it.state);
}
int main()
{
VectorStore vector;
for (auto& bin : vector)
{
std::cout << bin.value << "\n";
}
ListStore list;
for (auto& bin : list)
{
std::cout << bin.value << "\n";
}
}
The iterator state is stored in a std::variant, this requires that you know all of your derived classes in advance. If you want to use arbitrary derived classes you could store the state in std::any instead, the implementation would largely be the same:
#include <cstdint>
#include <vector>
#include <list>
#include <any>
#include <iostream>
struct Bin
{
int value;
};
class Store {
public:
virtual ~Store() = default;
// Iterators
class const_iterator;
virtual const_iterator begin() const = 0;
virtual const_iterator end() const = 0;
protected:
virtual void increment(std::any&) const = 0;
virtual const Bin& dereference(const std::any&) const = 0;
virtual bool equal(const std::any&, const std::any&) const = 0;
};
class Store::const_iterator
{
public:
bool operator == (const Store::const_iterator& other)
{
return store == other.store && store->equal(state, other.state);
}
bool operator != (const Store::const_iterator& other)
{
return !(*this == other);
}
Store::const_iterator& operator++()
{
store->increment(state);
return *this;
}
const Bin& operator*()
{
return store->dereference(state);
}
const Store* store;
std::any state;
};
class VectorStore : public Store {
public:
VectorStore()
:vector{{1},{2},{3},{4}}
{
}
const_iterator begin() const override;
const_iterator end() const override;
using const_iterator_state = std::vector<Bin>::const_iterator;
protected:
void increment(std::any&) const override;
const Bin& dereference(const std::any&) const override;
bool equal(const std::any&, const std::any&) const override;
private:
std::vector<Bin> vector;
};
Store::const_iterator VectorStore::begin() const {
return {this, vector.begin()};
}
Store::const_iterator VectorStore::end() const {
return {this, vector.end()};
}
void VectorStore::increment(std::any& state) const
{
state = ++std::any_cast<VectorStore::const_iterator_state>(state);
}
const Bin& VectorStore::dereference(const std::any& state) const
{
return *std::any_cast<VectorStore::const_iterator_state>(state);
}
bool VectorStore::equal(const std::any& lhs, const std::any& rhs) const
{
return std::any_cast<VectorStore::const_iterator_state>(lhs) == std::any_cast<VectorStore::const_iterator_state>(rhs);
}
class ListStore : public Store {
public:
ListStore()
:list{{2},{4},{6},{8}}
{
}
const_iterator begin() const override;
const_iterator end() const override;
using const_iterator_state = std::list<Bin>::const_iterator;
protected:
void increment(std::any&) const override;
const Bin& dereference(const std::any&) const override;
bool equal(const std::any&, const std::any&) const override;
private:
std::list<Bin> list;
};
Store::const_iterator ListStore::begin() const {
return {this, list.begin()};
}
Store::const_iterator ListStore::end() const {
return {this, list.end()};
}
void ListStore::increment(std::any& state) const
{
state = ++std::any_cast<ListStore::const_iterator_state>(state);
}
const Bin& ListStore::dereference(const std::any& state) const
{
return *std::any_cast<ListStore::const_iterator_state>(state);
}
bool ListStore::equal(const std::any& lhs, const std::any& rhs) const
{
return std::any_cast<ListStore::const_iterator_state>(lhs) == std::any_cast<ListStore::const_iterator_state>(rhs);
}
int main()
{
VectorStore vector;
for (auto& bin : vector)
{
std::cout << bin.value << "\n";
}
ListStore list;
for (auto& bin : list)
{
std::cout << bin.value << "\n";
}
}
I've only implemented the minimum for a for loop, you'll probably also want to implement the other iterator operators. Note that you'll probably only want to implement the common subset of operators supported by all of your container's iterators, e.g. in my sample code you wouldn't want to enable random access iterators as they aren't implementable for std::list.