迭代器在std::unordered_map<int,std::vector<Element>>

Iterator over std::unordered_map<int, std::vector<Element>>

本文关键字:gt std lt vector Element unordered int 迭代器 map      更新时间:2023-10-16

我有一个类,它在无序映射中存储一些数据。让我们假设类看起来如下:

class Container {
    std::unordered_map<int, std::vector<Element>> map;
};

为了方便起见,我想使用range for循环来迭代Container的内容,这样我就可以获得嵌套向量中的所有Elements

for (const Element& e : Container(...)) {
    magic(e);
}

要做到这一点,我需要实现迭代器,它将对嵌套向量的元素进行迭代。我试着自己做,但结果是一个出了故障的怪物,我甚至不敢把它放在这里。我只是提供了一个指向我编写的代码的链接,但它太糟糕了,无法正常工作,因为我无法正确实现end

所以我的问题是,它能以某种优雅的方式完成吗?或者至少是正确的?非常感谢您的帮助。

我很久以前就学会了一个技巧,可以将一个普通函数转换为一种生成器(或协程,或现在称之为什么)。

想象一下,C++有yield关键字,就像Python或C#一样。那么迭代就很简单了:

void shift()
{
    for (auto mapIt = bag->begin(); mapIt != bat->end(); ++mapIt)
    {   
        for (auto vecIt = mapIt->second.begin(); vecIt != mapIt->second.end(); ++vecIt)
        {   
            yield *vecIt; //Alas, C++ cannot yield!
        }
    }
}

转换为适当的C++函数可以半自动完成,遵循以下简单步骤:

1-将所有局部变量移到函数顶部:

void shift()
{
    mapIt_t mapIt; //assume the proper typedefs somewhere
    vecIt_t vecIt;
    for (mapIt = bag->begin(); mapIt != bat->end(); ++mapIt)
    {   
        for (vecIt = mapIt->second.begin(); vecIt != mapIt->second.end(); ++vecIt)
        {   
            yield *vecIt; //Alas, C++ cannot yield!
        }
    }
}

2-将局部变量转换为成员变量。添加一个int m_state;成员变量,初始化为0

mapIt_t m_mapIt;
vecIt_t m_vecIt;
int m_state = 0;
void shift()
{
    for (m_mapIt = bag->begin(); m_mapIt != bat->end(); ++m_mapIt)
    {   
        for (m_vecIt = m_mapIt->second.begin(); m_vecIt != m_mapIt->second.end(); ++m_vecIt)
        {   
            yield *m_vecIt; //Alas, C++ cannot yield!
        }
    }
}

3-将函数包装在switch (m_state)语句中。在开头放一个case 0:

mapIt_t m_mapIt;
vecIt_t m_vecIt;
int m_state = 0;
void shift()
{switch (m_state) { case 0: //behold my fancy indentation!
    for (m_mapIt = bag->begin(); m_mapIt != bat->end(); ++m_mapIt)
    {   
        for (m_vecIt = m_mapIt->second.begin(); m_vecIt != m_mapIt->second.end(); ++m_vecIt)
        {   
            yield *m_vecIt; //Alas, C++ cannot yield!
        }
    }
}}

4-将每个yield替换为以下语句:m_state = N; return; case N:;,因为N对于所使用的每个位置都是不同的整数。在函数末尾添加m_state = -1; return; case -1:;。如果你觉得值得,就使用宏。

mapIt_t m_mapIt;
vecIt_t m_vecIt;
int m_state = 0;
void shift()
{switch (m_state) { case 0: //behold my fancy indentation!
    for (m_mapIt = bag->begin(); m_mapIt != bat->end(); ++m_mapIt)
    {   
        for (m_vecIt = m_mapIt->second.begin(); m_vecIt != m_mapIt->second.end(); ++m_vecIt)
        {   
            m_state = 1; return; case 1:;
        }
    }
    m_state = -1; return; case -1:;
}}

5-完成!您可以使容器变得任意复杂:如果您可以编写一个完成完整迭代的函数,则可以将其转换为迭代器类。


考虑到所有这些,我已经编写了以下示例代码,这似乎很好。

class Bag
{
public:
    typedef std::unordered_map<int, std::vector<std::string> > container_t;
    container_t cnt;
};
class BagIterator : public std::iterator<std::forward_iterator_tag, std::string>
{
public:
    friend BagIterator begin(const Bag &bag);
    friend BagIterator end(const Bag &bag);
    BagIterator()
    {
        m_bag = NULL;
        m_state = -1;
    }
    const value_type &operator*() const
    {
        return *m_vecIt;
    }
    BagIterator &operator++()
    {
        shift();
        return *this;
    }
    BagIterator operator++(int)
    {
        BagIterator tmp = *this;
        operator++();
        return tmp;
    }
    bool operator==(const BagIterator &r) const
    {
        if (m_state != r.m_state)
            return false;
        if (m_state == -1)
            return true;
        return m_bag == r.m_bag && m_mapIt == r.m_mapIt && m_vecIt == r.m_vecIt;
    }
    bool operator!=(const BagIterator &r) const
    {
        return !operator==(r);
    }
private:
    typedef Bag::container_t::const_iterator mapIt_t;
    typedef std::vector<std::string>::const_iterator vecIt_t;
    const Bag *m_bag;
    mapIt_t m_mapIt;
    vecIt_t m_vecIt;
    int m_state;
    void shift()
    {switch (m_state) { case 0:
        for (m_mapIt = m_bag->cnt.begin(); m_mapIt != m_bag->cnt.end(); ++m_mapIt)
        {
            for (m_vecIt = m_mapIt->second.begin(); m_vecIt != m_mapIt->second.end(); ++m_vecIt)
            {
                m_state = 1; return; case 1:;
            }
        }
        m_state = -1; return; case -1:;
    }}
};
BagIterator begin(const Bag &bag)
{
    BagIterator res;
    res.m_bag = &bag;
    res.m_state = 0;
    res.shift();
    return res;
}
BagIterator end(const Bag &bag)
{
    BagIterator res;
    res.m_bag = &bag;
    return res;
}

一些评论:

  • 请注意我是如何从std::iterator派生自定义迭代器的,这样标准库就会知道它是如何工作的
  • 还请注意我是如何实现operator==()的。首先,它检查状态是否相同,然后如果两者都是end()m_state==-1),最后如果两者都指向同一个元素