6

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?

2
  • 1
    Instead of mItem(args...), write mItem(std::forward<Arguments>(args)...) for "perfect forwarding". And I guess you have similar logic in the implementation of AddItem. Commented Mar 3, 2015 at 13:10
  • What is the difference? Both versions seem to compile and run fine. Commented Mar 3, 2015 at 13:47

2 Answers 2

6

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" Items, 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()) {
    ...
}
Sign up to request clarification or add additional context in comments.

Comments

0

If you want the range-based for to work for your container, you have to provide begin and end functions that return forward iterators.

typedef std::vector<Item>::iterator iterator;
typedef std::vector<Item>::const_iterator const_iterator;
iterator begin()
{
   return mItems.begin();
}
const_iterator begin() const;
{
   return mItems.begin();
}
//also add end functions, and viola.

This will return the whole item struct. If you have to only return mItem, you'll have to write your own iterator adaptor and use it instead of vector's.

2 Comments

I want the later - how do I write my own iterator adaptor? And how can I keep it to a 'minimum'?
@KaiserJohaan See my answer for that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.