boost::multi_index 迭代器在擦除或修改作为不同索引的键的值时是否无效?

Are boost::multi_index iterators invalidated when erasing or modifying values that are the key of a different index?

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

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

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

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}