1

In testing it seems to work fine, but I could not find any mention of the expected behaviour in the documentation.

Essentially, if my multi_index_container has 2 ordered_non_unique indices using keys A and B respectively, if I iterate over a range from A and modify the B value (that might cause re-ordering), are the iterators for A invalidated?

Joaquín M López Muñoz
  • 5,243
  • 1
  • 15
  • 20
ozma
  • 349
  • 1
  • 3
  • 10

1 Answers1

3
  • Iterators are never invalidated as long as the element is not erased. Please note that invalidation is not the same as repositioning (caused by re-ordering).
  • Iterators to an index dependent on key A will not be invalidated nor repositioned (i.e., the index keeps its order) upon changes on a different key B, as long as the affected element is not erased (which can happen if the index dependent on key B is unique).
  • If you want to safely range over an A-index modifying B keys even in the case of erasures, you can do as exemplified below:

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);
}

Output

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

Expanded answer: When you're modifying the key of the index you're ranging on, you can either do a first pass to store all the iterators in the range before doing any actual modification (see modify_unstable_range here) or, in case you want to do the thing in just one pass, store the addresses of modified elements along the way to avoid revisitation:

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);
}

Output

before: {0,0}{1,1}{2,2}{3,3}{4,4}{5,5}
after: {0,0}{6,3}{8,4}{10,5}
Joaquín M López Muñoz
  • 5,243
  • 1
  • 15
  • 20
  • First of all, thanks for the answer. I swear I've seen your name like twenty times while searching for this, so bless you. Second, what about scenarios where the iterators would reposition then - I've read that you can store all the iterators and then modify them by iterating over them again, but is there a way to do this without iterating the same range twice? – ozma Dec 04 '19 at 01:40
  • Thank you again. Based on the modify_unstable_range example, is it guaranteed that modifying elements in a way that results in reordering/erasure of an element from that particular index will not invalidate the remaining saved iterators? – ozma Dec 06 '19 at 08:12
  • Yes, this is the case. – Joaquín M López Muñoz Dec 06 '19 at 11:02