![](/img/trans.png)
[英]Fast construction of unordered_map with nested vector with preallocation std::unordered_map<int, vector<Thing *>>?
[英]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-用以下语句替换每个yield
: m_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.