【问题标题】:Iterator invalidation - does end() count as an iterator or not?迭代器失效 - end() 是否算作迭代器?
【发布时间】:2013-10-16 00:27:34
【问题描述】:

我在使用std::multimap::equal_range()insert() 时遇到了以下问题。

根据cplusplus.comcppreference.comstd::multimap::insert 不会使任何迭代器失效,但下面的代码会导致无限循环:

#include <iostream>
#include <map>
#include <string>

int main(int argc, char* argv[])
{
    std::multimap<std::string,int> testMap;
    testMap.insert(std::pair<std::string,int>("a", 1));
    testMap.insert(std::pair<std::string,int>("a", 2));
    testMap.insert(std::pair<std::string,int>("a", 3));

    auto range = testMap.equal_range(std::string("a"));
    for (auto it = range.first; it != range.second; ++it)
    {
        testMap.insert(std::pair<std::string,int>("b", it->second));
        // this loop becomes infinite
    }

    // never gets here
    for (auto it = testMap.begin(); it != testMap.end(); ++it)
    {
        std::cout << it->first << " - " << it->second << std::endl;
    }
    return 0;
}

目的是使用特定键(在本例中为“a”)获取多图中的所有现有项目,并将它们复制到第二个键(“b”)下。实际上,第一个循环永远不会退出,因为it 永远不会匹配range.second。处理完映射中的第三个元素后,++it 让迭代器指向新插入的第一个元素。

我已经在 VS2012、Clang 和 GCC 上尝试过这个,所有编译器似乎都发生了同样的事情,所以我认为它是“正确的”。我是否对“没有迭代器或引用无效。”这句话读得太多了? end() 在这种情况下算不算迭代器?

【问题讨论】:

    标签: c++ c++11 iterator multimap


    【解决方案1】:

    multimap::equal_range 返回一个pair,在这种情况下,它的第二个元素是指向过去元素的迭代器(“这是容器的过去结束值”[container.requirements.general] /6)。

    我会稍微重写一下代码以指出一些事情:

    auto iBeg = testMap.begin();
    auto iEnd = testMap.end();
    
    for(auto i = iBeg; i != iEnd; ++i)
    {
        testMap.insert( std::make_pair("b", i->second) );
    }
    

    这里,iEnd 包含一个过去的迭代器。对multimap::insert 的调用不会使这个迭代器失效;它仍然是一个有效的结束迭代器。因此循环相当于:

    for(auto i = iBeg; i != testMap.end(); ++i)
    

    如果你不断添加元素,这当然是一个无限循环。

    【讨论】:

    • 有道理!我想问题是“迭代器没有失效”实际上是什么意思。迭代器本身仍然有效,但是如果使用它们进行迭代会导致意外行为,那么它并不像它们是否有效那么简单。
    • @JonathanPotter:“迭代器没有失效”意味着迭代器仍然有效,并且仍然引用容器的相同元素(或者在结束迭代器的情况下,仍然引用容器的末端)。您可以从中推断(并期望)正确的行为。
    • @JonathanPotter 我仍在努力寻找一个优雅的解决方案。也许是like this
    • @DyP:很好!这比我想出的要好,即先将新元素粘贴到临时向量中。
    • 芝诺在坟墓里翻了个身,多亏了这个。
    【解决方案2】:

    结束迭代器range.second 没有失效。

    循环无限的原因是循环体的每次重复:

    • 在地图末尾插入一个新元素,从而将it 和末尾之间的距离增加一倍(因此,在此插入之后,range 不再代表键 "a"equal_range因为您在它所代表的范围内插入了一个新键,从第一个 "a" 到容器的末尾)。
    • 递增it,将it 与结尾之间的距离减一。

    因此,it 永远不会到达终点。

    我可以这样写你想要的循环:

    for (auto it = testMap.lower_bound("a"); it != testMap.end() && it->first == "a"; ++it)
    {
         testMap.insert(std::pair<std::string,int>("b", it->second));
    }
    

    【讨论】:

      【解决方案3】:

      使其按预期工作的解决方案(随时改进,这是一个社区 wiki)

      auto range = testMap.equal_range(std::string("a"));
      if(range.first != range.second)
      {
          --range.second;
          for (auto it = range.first; it != std::next(range.second); ++it)
          {
              testMap.insert(std::pair<std::string,int>("b", it->second));
          }
      }
      

      【讨论】:

      • 注:这只有在您插入 时才是安全的。当您删除时,您可能会使range.second无效。
      • 就此而言,当您删除时,it 可能会失效,具体取决于您删除的内容:-)
      猜你喜欢
      • 1970-01-01
      • 2014-11-13
      • 2023-01-13
      • 1970-01-01
      • 2017-02-23
      • 1970-01-01
      • 2013-05-30
      • 2015-04-29
      • 1970-01-01
      相关资源
      最近更新 更多