我有一个类在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
而无法正常工作。
所以我的问题是,它能以优雅的方式完成吗?或者至少是正确的?任何帮助深表感谢。
答案 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 - 使用以下语句替换每个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
),最后两者都指向相同的元素。