迭代器在std::unordered_map<int,std::vector<Element>>
Iterator over std::unordered_map<int, std::vector<Element>>
我有一个类,它在无序映射中存储一些数据。让我们假设类看起来如下:
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
),最后如果两者都指向同一个元素
相关文章:
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- std::向量与传递值的动态数组
- 使用std::vector的OpenCL矩阵乘法
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- std::condition_variable::wait()如何评估给定的谓词
- 如何获取std::result_of函数的返回类型
- std::原子加载和存储都需要吗
- 将对象移动到std::shared_ptr
- std::vector<;uint8_t>;当C++11/14启用时,手动复制而不是调用memcpy
- 是std :: set&lt; std :: future&gt;不可能存在
- 在修改列表后,std :: list&lt; t&gt; :: end()的值是否会更改
- 使用 std::vector<boost::shared_ptr<Base_Class>> 或 boost::p tr_vector 的性能注意事项是什么<Base>
- std::map<std::set, double> AND std:<long>map< std::p air<long, long>, double>
- 如何获取std::vector<DMatch>