繁体   English   中英

在std :: unordered_map上进行迭代 <int, std::vector<Element> &gt;

[英]Iterator over std::unordered_map<int, std::vector<Element>>

我有一个在unordered_map中存储一些数据的类。 让我们假设该类如下所示:

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

为了方便起见,我想使用range-for-loop遍历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-用以下语句替换每个yieldm_state = N; return; case N:; m_state = N; return; case N:; ,对于每个使用的位置N一个不同的整数。 m_state = -1; return; case -1:; 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派生自定义迭代std::iterator以便标准库知道其工作方式。
  • 还要注意我如何实现operator==() 首先,它检查状态是否相同,然后检查两个状态是否均为end()m_state==-1 ),最后检查是否均指向同一元素。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM