C++ 我可以将反向迭代器转换为正向迭代器吗?

C++ 我可以将反向迭代器转换为正向迭代器吗?,c++,iterator,C++,Iterator,我有一个名为Action的类,它本质上是一个围绕着一组Move对象的包装器 因为我需要遍历向前和向后移动的deque,所以我有一个向前迭代器和一个反向迭代器作为类的成员变量。这样做的原因是因为我需要知道我什么时候走到了终点,无论是前进还是后退 该类如下所示: class Action { public: SetMoves(std::deque<Move> & dmoves) { _moves = dmoves; } void Advance(); bo

我有一个名为Action的类,它本质上是一个围绕着一组Move对象的包装器

因为我需要遍历向前和向后移动的deque,所以我有一个向前迭代器和一个反向迭代器作为类的成员变量。这样做的原因是因为我需要知道我什么时候走到了终点,无论是前进还是后退

该类如下所示:

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;
};
#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";
}
我的问题是,我希望能够检索到当前移动对象的迭代器,而不需要查询我是前进还是后退。这意味着一个函数返回一种类型的迭代器,但我有两种类型


我是否应该忘记返回迭代器,而是返回对移动对象的常量引用?

也许您应该重新考虑容器的选择

通常,您不需要使用反向迭代器进行反向运算

currentfwd--
将会倒退,尽管它可能不起作用,我想你已经尝试过退出队列了


您真正应该做的是在这里将您的类建模为出列的装饰器,并实现您自己的动作迭代器。无论如何,我都会这样做。

反向迭代器有一个成员,它返回一个相应的正向迭代器。请注意,这不是引用同一对象的迭代器-它实际上引用序列中的下一个对象。这样,rbegin对应于end,rend对应于begin

因此,如果您想返回一个迭代器,那么您可以执行以下操作

std::deque<Move>::const_iterator Current() const
{
    if (forward)
        return currentfwd;
    else
        return (currentbck+1).base();
}

不过,我更愿意返回一个引用,并将所有迭代细节封装在类中。

因为它与std::vector相同,所以对于两次遍历,最好在deque中使用一个整数索引。

在我看来,在同一个类中实际上有两种不同的行为

值得注意的是,您似乎只能按一个顺序遍历集合,否则,如果您开始遍历,然后更改bforward参数,您将遇到一个非常奇怪的情况

就个人而言,我完全支持公开两个迭代器,即前向开始、结束、rbegin和rend

您还可以返回一个简单的迭代器对象:

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;
};
或者,如果您想在正向和反向遍历之间动态选择,您可以将迭代器设置为纯虚拟接口,同时具有正向和反向遍历

但实际上,如果看起来您只会使用一个,那么我真的看不到同时存储正向和反向迭代器的意义:/

这正是促使STL设计开始的那种问题。有真正的原因:不将迭代器与容器一起存储使用接受任意迭代器的算法让算法一次计算整个范围而不是单个项

我怀疑你现在看到的或多或少是真正问题的冰山一角。我的建议是退后一步,而不是询问如何处理当前设计的细节,而是询问一个更一般的问题,即您试图实现什么,以及如何最好地实现最终结果

对于那些主要关心标题中问题的人来说,答案是完全限定的“是”。特别是,反向迭代器有一个基本成员来实现这一点。不过,这些条件有些问题

演示问题,考虑这样的代码:

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;
};
#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";
}
摘要:对于rbegin,我们必须在转换为正向迭代器之前添加一个迭代器,以获得有效的迭代器-但是对于rend,我们不能在转换为有效迭代器之前添加一个迭代器

只要使用X.rbegin和X.rend作为通用算法的参数,这很好,但经验表明,转换为正向迭代器通常会导致问题


然而,最后,对于问题的主体(而不是标题),答案与上面给出的基本相同:问题源于尝试创建一个对象,该对象将集合与两个迭代器组合到该集合中。如果解决了这个问题,那么使用正向和反向迭代器的整个业务将变得毫无意义。

谢谢Charles。返回的问题是在完成的函数中-当我在第一个元素之前(即已通过rend)时,我需要knwo。谢谢-正是出于这个原因,我一直在应用程序的其余部分使用deque。我不知道为什么我对迭代器有一种隧道式的看法:-这就是为什么你总是需要第二双眼睛:但要小心:测试一个无符号整数是否达到了零下值是相当困难的;您可以使用前向迭代器,如果它等于end,请小心不要增加它,如果等于end,请不要减少它。不管怎样,都要注意使迭代器失效的操作,因为它们也会使索引eit失效
因为它不再指向你认为它所指向的元素,或者因为当索引指向deque的结尾时你删除了一些东西。你的意思是如果等于开始,就不递减吗?问题是,如果Advance没有转到与rend等效的函数,则GetCurrentMove之类的函数将在实际处理完所有移动后返回begin。我喜欢这个解决方案,它可能是一个很好的学习练习。存储这两个迭代器的原因是从应用程序中的不同位置调用GetCurrentMove以前进。因此,我需要一个方便的地方来存储当前的移动。这通常是迭代器的作用,虽然它在C++中实现为2个不同的对象,虽然它节省了一些地方,并且MIMICK指针算法,但我觉得很烦人。上面的迭代器的灵感来自Python>一个保持其当前位置和端点的单个对象。在应用程序中,不需要传递完整的Action类,只需传递迭代器,如果要直接引用deque迭代器、当前位置和结束。通过这种方式,您可以促进解耦:当currentbck是一个结束迭代器时,currentbck+1.base会崩溃。在两者之间转换是一个等待发生的错误世界。我喜欢你的答案。你可能是对的。对于C++和STL,我比较新。什么是好的C++设计是我努力学习的东西。虽然这个答案帮助BeeBand它没有回答后代的原始问题。这个答案应该是对原始帖子的评论。我会让BeeBand考虑把滴答声改为Mike Seymour的回答。卢卡斯:如果你把问题限制在标题中,你是对的。然而,如果你阅读了整个问题本身,我最多只能说很少。四年后,我会说人们发现这个问题更多的是因为它的标题,而不是因为它的内容。再过五年,情况就更糟了。^你的问题的答案是,我能从正向迭代器中得到反向迭代器吗