[英]Can I convert a reverse iterator to a forward iterator?
我有一个名为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
对象的迭代器,而无需查询我是向前还是向后。 这意味着一个函数返回一种类型的迭代器,但我有两种类型。
我应该忘记返回迭代器,而是返回对Move
对象的 const 引用吗?
反向迭代器有一个成员base()
,它返回一个相应的正向迭代器。 请注意,这不是引用同一对象的迭代器 - 它实际上是指序列中的下一个对象。 这是因为rbegin()
对应于end()
并且rend()
对应于begin()
。
所以如果你想返回一个迭代器,那么你会做类似的事情
std::deque<Move>::const_iterator Current() const
{
if (forward)
return currentfwd;
else
return (currentbck+1).base();
}
不过,我更愿意返回一个引用,并将所有迭代细节封装在类中。
这正是促使 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()
我们不能在转换为一个有效的迭代器之前添加一个。
只要您使用X.rbegin()
和X.rend()
作为泛型算法的参数,那很好——但经验表明,转换为前向迭代器通常会导致问题。
然而,最后,对于问题的主体(而不是标题),答案几乎与上面给出的一样:问题源于试图创建一个对象,该对象将集合与几个迭代器组合到该集合中. 解决这个问题,整个带有正向和反向迭代器的业务就变得没有意义了。
由于std::deque
是一个随机访问容器(与std::vector
相同),因此您最好在两次遍历中使用单个整数索引进入双端队列。
在我看来,您实际上在同一个班级中有两种不同的行为。
值得注意的是,您似乎只能以一种顺序遍历您的集合,否则如果您要开始遍历然后更改bforward
参数,您最终会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 成为一个纯虚拟接口并同时具有向前和向后遍历。
但实际上,如果看起来你只会使用一个,我真的不明白同时存储向前和向后迭代器的意义:/
也许您应该重新考虑对容器的选择。
通常你不需要使用反向迭代器来倒退,
currentfwd--
会倒退,尽管它可能无法与出队一起工作(我假设您尝试过)。
您真正应该做的是将您的类建模为出队的装饰器并实现您自己的 Action 迭代器。 无论如何,这就是我要做的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.