Reputation: 5523
I have a pure virtual interface to a container which is more or less like this:
class IContainer
{
public:
virtual ~IContainer() = default;
virtual Element& operator[](size_t index) = 0;
virtual const Element& operator[](size_t index) const = 0;
virtual size_t size() const = 0;
};
I would like to make use of range for loops, so I need to define begin() and end(). In order to do so, I need to define the iterator type as well.
It should be not be particularly hard, but nevertheless I would like to know if is there already anything in STL or Boost that can come to help, before I start coding something that already exists.
Upvotes: 5
Views: 717
Reputation: 275385
It might not be a good idea, but adding for(:)
loop support is relatively easy here. I'll be minimal.
I'll create an iteroid
, a not-iterator that is enough to support for(:)
loops. This requires ++
, !=
and unary *
support, and nothing else.
template<class C>
struct index_iteroid {
decltype(auto) operator*()const {
return (*container)[i];
}
index_iteroid(index_iteroid const&)=default;
index_iteroid& operator=(index_iteroid const&)=default;
friend bool operator==(index_iteroid const& lhs, index_iteroid const& rhs) {
return std::tie(lhs.i, lhs.container)==std::tie(rhs.i, rhs.container);
}
friend bool operator!=(index_iteroid const& lhs, index_iteroid const& rhs) {
return !(lhs==rhs);
}
void operator++()&{
++i;
}
index_iteroid(C* c, std::size_t in):i(in), container(c) {}
private:
std::size_t i = 0;
C* container = nullptr;
};
now we use it:
class IContainer
{
public:
virtual ~IContainer() = default;
virtual Element& operator[](size_t index) = 0;
virtual const Element& operator[](size_t index) const = 0;
virtual size_t size() const = 0;
index_iteroid<IContainer> begin() { return {this, 0}; }
index_iteroid<IContainer> end() { return {this, size()}; }
index_iteroid<IContainer const> begin() const { return {this, 0}; }
index_iteroid<IContainer const> end() const { return {this, size()}; }
};
and there you have it.
void test( IContainer* cont ) {
if (!cont) return;
for(Element& e : *cont) {
// code
}
}
please excuse any typos.
Now a full iterator takes about 2-3 times as much code as my iteroid
does, but nothing tricky, just annoying boilerplate mostly.
The standard doesn't have much to help you. For boost, you could compose a counting iterator with a function caling iterator/generator, and have the function call use []
. Boost also has some utilities to make it take less boilerplate to write a full iterator, if you want to upgrade the iteroid to an iterator.
Upvotes: 3
Reputation: 62686
C++ doesn't do "interfaces" like this. The idiomatic way is for the (potential) clients of IContainer
to instead be templated over the container type and just call values[index]
, or be templated over an iterator type and call like *(first + offset)
.
In C++20 you will be able to write a Container
Concept, which behaves somewhat like an interface definition, but you can already express a Concept as documented requirements.
If instead you want a type-erased random access "container", you can use boost::any_range<Element, boost::random_access_traversal_tag>
Upvotes: 1