Iterator over std :: unordered_map <int,std :: vector <element =“”>&gt;

时间:2016-05-08 20:48:01

标签: c++ c++11 for-loop iterator

我有一个类在unordered_map中存储一些数据。我们假设该类看起来如下:

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

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

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

为此,我需要实现迭代器,迭代嵌套向量的元素。我试图自己做,但结果是这样一个故障的怪物,我甚至不敢把它放在这里。我只是为我编写的代码提供link,但它很糟糕,但由于我无法正确实现end而无法正常工作。

所以我的问题是,它能以优雅的方式完成吗?或者至少是正确的?任何帮助深表感谢。

1 个答案:

答案 0 :(得分:1)

我很久以前就学会了将一个普通函数转换为一种生成器(或者协程,或者这些天所谓的)。

想象一下,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:;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),最后两者都指向相同的元素。