I have a custom container which I want to use in a ranged-based for loop. The container is somewhat based on a vector, like this:
template<typename T>
class IDMap
{
private:
struct Item {
uint16_t mVersion;
T mItem;
template <typename... Arguments>
Item(uint16_t version, Arguments&&... args) : mVersion(version), mItem(args...)
{
}
};
public:
typedef uint32_t ItemID;
template <typename... Arguments>
ItemID AddItem(Arguments&&... args);
void MarkAsFree(const ItemID id);
T& GetItem(const ItemID id);
T* TryGetItem(const ItemID id);
void Clear();
private:
std::vector<Item> mItems;
std::vector<uint16_t> mFreeIndices;
};
I want to iterate the mItems
vector, but only return the mItem
member rather than the entire Item
struct. Is there any easy/elegant way to do this?
You have to provide a begin
and end
function, both returning a corresponding iterator, which itself implements operators ++
, !=
and *
. The begin
and end
functions can be free-standing or as members.
Start with implementing an iterator which has the behavior you want. You can implement it as a wrapper around a std::vector::iterator
to save most of the "core" work.
The following is untested code
Basically, inside class IDMap, add:
class ItemIterator {
// based on vector iterator
std::vector<Item>::iterator i;
public:
ItemIterator(std::vector<Item>::iterator i) : i(i) {}
// incrementing
ItemIterator & operator ++() { ++i; return *this; }
ItemIterator operator ++(int) { const_iterator old(*this); ++(*this); return old; }
// comparison
bool operator!=(const ItemIterator &o) const { return i != o.i; }
// dereferencing
const T & operator*() const { return i->mItem; }
};
using iterator = ItemIterator;
using value_type = T;
ItemIterator begin() const { return ItemIterator(mItems.begin()); }
ItemIterator end() const { return ItemIterator(mItems.end() ); }
If you ever want to support multiple kinds of "special iteration" over your IDMap, like for example also over the indices, or over the "whole" Item
s, you should wrap everything above in another adaptor. This adaptor can then be accessed with a member method, like .items()
.
Brief example:
class IDMap {
// (your code)
public:
struct ItemsAdaptor {
// (insert above iterator definition + usings)
ItemsAdaptor(std::vector<Item>::iterator b,
std::vector<Item>::iterator e)
: b{b}, e{e}
{}
ItemIterator begin() const { return b; }
ItemIterator end() const { return e; }
private:
ItemIterator b, e;
};
ItemsAdaptor items() const {
return ItemsAdaptor(mItems.begin(), mItems.end());
}
};
Then, you can write:
IDMap<int> map = ...;
for (int i : map.items()) {
...
}
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