我有一个名为Action
的类,它基本上是Move
个对象的转义包装。
因为我需要向前和向后遍历Moves
的双端队列,所以我有一个前向迭代器和一个reverse_iterator作为该类的成员变量。这样做的原因是因为当我前往或后退时,我需要知道当我走过双端的“结束”时。
该课程如下:
class Action
{
public:
SetMoves(std::deque<Move> & dmoves) { _moves = dmoves; }
void Advance();
bool Finished()
{
if( bForward )
return (currentfwd==_moves.end());
else
return (currentbck==_moves.rend());
}
private:
std::deque<Move> _moves;
std::deque<Move>::const_iterator currentfwd;
std::deque<Move>::const_reverse_iterator currentbck;
bool bForward;
};
Advance
功能如下:
void Action::Advance
{
if( bForward)
currentfwd++;
else
currentbck++;
}
我的问题是,我希望能够检索当前Move
对象的迭代器,而无需查询我是向前还是向后。这意味着一个函数返回一种类型的迭代器,但我有两种类型。
我是否应该忘记返回迭代器,并将const引用返回给Move
对象?
祝福,
BeeBand
答案 0 :(得分:63)
反向迭代器有一个成员base()
,它返回一个相应的前向迭代器。请注意,此不是引用同一对象的迭代器 - 它实际上是指序列中的下一个对象。这样rbegin()
与end()
对应,rend()
与begin()
对应。
因此,如果你想返回一个迭代器,那么你会做类似
的事情std::deque<Move>::const_iterator Current() const
{
if (forward)
return currentfwd;
else
return (currentbck+1).base();
}
我宁愿返回一个引用,并将所有迭代细节封装在类中。
答案 1 :(得分:27)
这是完全那种促使STL设计开始的问题。有以下真实原因:
我怀疑你现在看到的东西或多或少是真正问题的冰山一角。我的建议是退后一步,而不是询问如何处理目前设计的细节,而是询问一个更为普遍的问题,即你要完成什么,以及如何最好地完成最终结果。
对于那些主要关注标题中问题的人来说,答案是严格限定的“是”。特别是,reverse_iterator具有base()
成员。但资格有些问题。
演示问题,考虑这样的代码:
#include <iostream>
#include <vector>
#include <iterator>
int main() {
int i[] = { 1, 2, 3, 4};
std::vector<int> numbers(i, i+4);
std::cout << *numbers.rbegin() << "\n";
std::cout << *numbers.rbegin().base() << "\n";
std::cout << *(numbers.rbegin()+1).base() << "\n";
std::cout << *numbers.rend() << "\n";
std::cout << *numbers.rend().base() << "\n";
std::cout << *(numbers.rend()+1).base() << "\n";
}
在我的特定机器上的这个特定时刻运行它会产生以下输出:
4
0
4
-1879048016
1
-1879048016
摘要:使用rbegin()
我们必须在转换为转发迭代器之前添加一个以获得有效的迭代器 - 但是rend()
我们必须不< / em>在转换之前添加一个以获得有效的迭代器。
只要您使用X.rbegin()
和X.rend()
作为通用算法的参数,那就没问题 - 但经验表明转换为转发迭代器通常会导致问题。
然而,最后,对于问题的主体(与标题相对),答案几乎与上面给出的一样:问题源于尝试创建一个将集合与几个迭代器组合在一起的对象进入那个系列。解决这个问题,使用正向和反向迭代器的整个业务变得毫无意义。
答案 2 :(得分:6)
由于std::deque
是random access container(与std::vector
相同),因此对于两次遍历,使用单个整数索引进入双端队列会更好。
答案 3 :(得分:1)
在我看来,你在同一个班级中实际上有两种不同的行为。
值得注意的是,您似乎只能在一个订单中遍历您的集合,否则如果您要开始遍历然后更改bforward
参数,则最终会出现相当奇怪的情况。
就个人而言,我完全是为了暴露两个迭代器(即转发begin, end, rbegin and rend
)。
您还可以返回一个简单的Iterator对象:
template <class T>
class Iterator
{
public:
typedef typename T::reference_type reference_type;
Iterator(T it, T end) : m_it(it), m_end(end) {}
operator bool() const { return m_it != m_end; }
reference_type operator*() const { return *m_it; }
Iterator& operator++() { ++m_it; return *this; }
private:
T m_it;
T m_end;
};
template <class T>
Iterator<T> make_iterator(T it, T end) { return Iterator<T>(it,end); }
然后,您可以返回这个简单的对象:
class Action
{
public:
Action(std::deque<Move> const& d): m_deque(d) {} // const& please
typedef Iterator< std::deque<Move>::iterator > forward_iterator_type;
typedef Iterator< std::deque<Move>::reverse_iterator > backward_iterator_type;
forward_iterator_type forward_iterator()
{
return make_iterator(m_deque.begin(), m_deque.end());
}
backward_iterator_type backward_iterator()
{
return make_iterator(m_deque.rbegin(), m_deque.rend());
}
private:
std::deque<Move> m_deque;
};
或者,如果要在前向和后向遍历之间动态选择,可以使Iterator成为纯虚拟接口并同时进行前向和后向遍历。
但实际上,如果看起来你只使用一个,我真的没有看到存储BOTH向前和向后迭代器的重点:/
答案 4 :(得分:0)
也许你应该重新考虑你选择的容器。
通常你不需要使用反向迭代器来反向,
currentfwd--
会向后退,虽然它可能不起作用(我假设你尝试过),但是它会出现。
你应该做的是在这里将你的类建模为dequeue的装饰器并实现你自己的Action迭代器。无论如何,这将是我要做的。