首页 > 解决方案 > 擦除或修改作为不同索引键的值时,boost::multi_index 迭代器是否无效?

问题描述

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

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

标签: c++loopsiteratorcontainersboost-multi-index

解决方案


  • 只要元素未被擦除,迭代器就永远不会失效。请注意,失效与重新定位(由重新排序引起)不同。
  • 依赖于键 A 的索引的迭代器不会在不同键 B 上发生更改时失效或重新定位(即,索引保持其顺序),只要不擦除受影响的元素(如果索引依赖于键,则可能发生这种情况B 是唯一的)。
  • 即使在擦除的情况下,如果您想安全地覆盖 A-index 修改 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_range 此处),或者,如果您想一次性完成,沿途存储修改元素的地址以避免重新访问:

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}

推荐阅读