【问题标题】:Are boost::multi_index iterators invalidated when erasing or modifying values that are the key of a different index?擦除或修改作为不同索引键的值时,boost::multi_index 迭代器是否无效?
【发布时间】:2020-03-28 11:24:12
【问题描述】:

在测试中它似乎工作正常,但我在文档中找不到任何关于预期行为的提及。

基本上,如果我的 multi_index_container 分别使用键 A 和 B 有 2 个 ordered_non_unique 索引,如果我从 A 迭代一个范围并修改 B 值(这可能导致重新排序),A 的迭代器是否无效?

【问题讨论】:

    标签: c++ loops iterator containers boost-multi-index


    【解决方案1】:
    • 只要元素未被擦除,迭代器就永远不会无效。请注意,失效与重新定位(由重新订购引起)不同。
    • 依赖于键 A 的索引的迭代器不会在不同键 B 上发生更改时失效或重新定位(即,索引保持其顺序),只要不删除受影响的元素(如果索引可能会发生这种情况)依赖于键 B 是唯一的)。
    • 如果即使在擦除的情况下,您也想安全地覆盖 A 索引修改 B 键,您可以按照以下示例进行操作:

    Live On Wandbox

    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/key.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    #include <iostream>
    #include <iterator>
    
    using namespace boost::multi_index;
    
    struct element
    {
      int a;
      int b;
    };
    
    using container=multi_index_container<
      element,
      indexed_by<
        ordered_unique<key<&element::a>>,
        ordered_unique<key<&element::b>>
      >
    >;
    
    int main()
    {
      container c={{0,0},{1,1},{2,2},{3,3},{4,4},{5,5}};
    
      auto print=[](auto& c){
        for(const auto& x:c)std::cout<<"{"<<x.a<<","<<x.b<<"}";
        std::cout<<"\n";
      };
    
      std::cout<<"before: ";
      print(c);
    
      for(auto first=c.begin(),last=c.end();first!=last;){
        // we get next position now in case first will be invalidated
        auto next=std::next(first);
        c.modify(first,[](auto& x){
          x.b*=2;
        });
        first=next;
      }
    
      std::cout<<"after: ";
      print(c);
    }
    

    输出

    before: {0,0}{1,1}{2,2}{3,3}{4,4}{5,5}
    after: {0,0}{3,6}{4,8}{5,10}
    

    扩展答案:当您修改您要测距的索引的键时,您可以在进行任何实际修改之前先执行一次以将所有迭代器存储在该范围内(请参阅modify_unstable_rangehere) 或者,如果您只想一次性完成此操作,请在此过程中存储修改元素的地址以避免重新访问:

    Live On Wandbox

    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/key.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    #include <iostream>
    #include <iterator>
    #include <unordered_set>
    
    using namespace boost::multi_index;
    
    struct element
    {
      int a;
      int b;
    };
    
    using container=multi_index_container<
      element,
      indexed_by<
        ordered_unique<key<&element::a>>,
        ordered_unique<key<&element::b>>
      >
    >;
    
    int main()
    {
      container c={{0,0},{1,1},{2,2},{3,3},{4,4},{5,5}};
    
      auto print=[](auto& c){
        for(const auto& x:c)std::cout<<"{"<<x.a<<","<<x.b<<"}";
        std::cout<<"\n";
      };
    
      std::cout<<"before: ";
      print(c);
    
      std::unordered_set<const element*> visited;
      for(auto first=c.begin(),last=c.end();first!=last;){
        // we get next position now before first is invalidated/repositioned
        auto next=std::next(first);
        if(c.modify(first,[](auto& x){
          x.a*=2; // note we're modifying the key of the index we're at
        })){
          // element succesfully modified, store address to avoid revisitation
          visited.insert(&*first);
        }
        // move to next nonvisited element
        first=next;
        while(first!=last&&visited.find(&*first)!=visited.end())++first;
      }
    
      std::cout<<"after: ";
      print(c);
    }
    

    输出

    before: {0,0}{1,1}{2,2}{3,3}{4,4}{5,5}
    after: {0,0}{6,3}{8,4}{10,5}
    

    【讨论】:

    • 首先感谢您的回答。我发誓我在搜索这个时已经看到你的名字二十次了,所以祝福你。其次,迭代器将重新定位的场景怎么样 - 我已经读过您可以存储所有迭代器,然后通过再次迭代它们来修改它们,但是有没有办法在不重复相同范围两次的情况下做到这一点?跨度>
    • 扩展答案
    • 再次感谢您。根据 modify_unstable_range 示例,是否可以保证以一种导致从该特定索引重新排序/擦除元素的方式修改元素不会使剩余的已保存迭代器无效?
    • 是的,就是这样。
    猜你喜欢
    • 1970-01-01
    • 2016-03-23
    • 1970-01-01
    • 2013-06-24
    • 1970-01-01
    • 2014-10-23
    • 2021-03-30
    • 1970-01-01
    • 2011-02-14
    相关资源
    最近更新 更多