基于自定义容器范围的迭代

Custom container range-based iteration

本文关键字:范围 迭代 自定义      更新时间:2023-10-16

我有一个自定义容器,我想在基于范围的for循环中使用它。容器在某种程度上是基于向量的,比如:

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;
};

我想迭代mItems向量,但只返回mItem成员,而不是整个Item结构。有什么简单/优雅的方法可以做到这一点吗?

您必须提供beginend函数,这两个函数都返回相应的迭代器,迭代器本身实现运算符++!=*beginend功能可以是独立的或作为成员。

首先实现一个迭代器,它具有您想要的行为。您可以将其实现为std::vector::iterator的包装器,以节省大部分"核心"工作。

以下是未经测试的代码

基本上,在类IDMap中,添加:

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()  ); }

如果您希望在IDMap上支持多种类型的"特殊迭代",例如在索引上或在"整个"Item上,则应该将上面的所有内容都封装在另一个适配器中。然后可以使用成员方法访问该适配器,如.items()

简要示例:

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());
    }
};

然后,你可以写:

IDMap<int> map = ...;
for (int i : map.items()) {
    ...
}

如果您希望基于的范围适用于您的容器,您必须提供返回前向迭代器的beginend函数。

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.

这将返回整个项结构。如果必须只返回mItem,则必须编写自己的迭代器适配器,并使用它来代替vector。