【问题标题】:Iterating over a container bidirectionally双向迭代容器
【发布时间】:2019-01-17 06:38:26
【问题描述】:

有没有比下面的代码更好的方法,使用相同的迭代器在任一方向上迭代容器?

#include <iostream>
#include <map>

int main()
{
    const bool descend = false;

    std::map<int, int> mapp;
    mapp[1] = 1;
    mapp[2] = 2;
    mapp[3] = 3;
    mapp[4] = 4;

    std::map<int, int>::iterator startIter = descend ? --(mapp.end()) : mapp.begin();
    std::map<int, int>::iterator endIter = descend ? --(mapp.begin()) : mapp.end();

    while (startIter != endIter)
    {
        std::cout << startIter->first << std::endl;
        descend ? --startIter : ++startIter;
    }
}

【问题讨论】:

  • rbegin()rend() ?
  • 任何工作方式都会更好,因为这是 UB:--(mapp.begin())
  • @Slava 真的吗? rend 会在开始之前指向什么?
  • @NathanOliver rend() 应该是从 begin() 创建的反向迭代器,同样的方式 rbegin() 是从 end() 创建而不是 end() - 1
  • @NathanOliver 不,你错了 - en.cppreference.com/w/cpp/iterator/reverse_iterator/operator* “将元素的引用或指针返回到当前元素上一个。”

标签: c++ c++11 iterator stdmap


【解决方案1】:

您的代码无效,因为此语句 --(mapp.begin()) 导致 UB。我会写一个薄包装:

template<class Iter, class F>
void apply( Iter begin, Iter end, F f, bool forward )
{
    while( begin != end ) 
        f( forward ? *begin++ : *--end );
}

live example

或者只是简单地将你的循环重写为:

auto begin = mapp.begin();
auto end = mapp.end();
while ( begin != end)
{
    const auto &p = forward ? *begin++ : *--end;
    std::cout << p.first << std::endl;
}

【讨论】:

    【解决方案2】:

    有没有比下面的代码更好的方法来迭代容器 在任一方向,使用相同的迭代器?

    是的。使用std::map::reverse_iterator。这将是比您发布的代码更好的方法,但将不再使用相同的迭代器,这是您的要求之一。

    但是,这将比您编写的代码更不容易出错。除此之外,如果已经在 C++ 中,您不需要重新发明轮子。

    See output here

    #include <iostream>
    #include <map>
    
    template<typename Iterator>
    void print(const Iterator Begin, const Iterator End)
    {
        for(Iterator iter = Begin; iter != End; ++iter)
           std::cout << iter->first << "\n";
    }
    
    int main()
    {
        const bool descend = true;
    
        std::map<int, int> mapp;
        mapp[1] = 1;
        mapp[2] = 2;
        mapp[3] = 3;
        mapp[4] = 4;
    
        descend ?
            print(mapp.crbegin(), mapp.crend()):
            print(mapp.cbegin(), mapp.cend());
        return 0;
    }
    

    cppreference.com 的图片将以图形方式解释它是如何工作的。

    【讨论】:

    • 此代码不满足 OP 的要求“使用 same 迭代器”
    • @Slava 真。我没有想过要制作它,而是想展示一个替代方案,它缺乏 OPs 要求。好吧,现在他有正确的答案(来自你)和另一个选择(来自我);)
    • 有没有办法扩展它,以便我可以擦除循环中的迭代器并继续循环?问题是所需的代码不同,因为 erase() 用于前向迭代器,因此由于模板而导致编译器错误。
    • @user997112 是的。然后你需要将std::map 传递给模板,因为std::map::erse 需要知道地图。迭代器将不起作用。我发现的最佳解决方案是:remove_if equivalent for std::mapHow to call erase with a reverse iterator
    • @JeJo 你的第一个链接没有引用反向迭代器,你的第二个链接没有引用前向迭代器,因此我目前的问题:)
    【解决方案3】:

    编写自文档化的代码,它就变得简单了。将该循环分解为它自己的函数并使用适当的迭代器调用它。

    这就是我们有“反向迭代器”的原因,它们可用于通过使用正常的前向语义来向后遍历容器。

    #include <iostream>
    #include <map>
    
    template<typename I>
    void printMapContainer(I begin, I end)
    {
        for (;begin != end; ++begin)
        {
            std::cout << begin->first << "\n";
        }
    }
    int main()
    {
        const bool descend = false;
    
        std::map<int, int> mapp;
        mapp[1] = 1;
        mapp[2] = 2;
        mapp[3] = 3;
        mapp[4] = 4;
    
        if (descend) {
            printMapContainer(mapp.rbegin(), mapp.rend());
        }
        else {
            printMapContainer(mapp.begin(), mapp.end());
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-08
      • 2015-07-14
      • 1970-01-01
      • 2012-10-27
      • 2012-02-20
      • 2015-07-13
      • 2018-08-07
      相关资源
      最近更新 更多