Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting a non-const iterator in Boost's multi index container

Tags:

c++

boost

Using Boost 1_33_1, I get an error implying that my iterator is a const iterator (because it wont let me deref the result from find()).

$ g++ bmi_iter_tst.cpp 
bmi_iter_tst.cpp: In function ‘void tst(employee_set&)’:
bmi_iter_tst.cpp:32: error: invalid initialization of reference of type ‘employee&’ from expression of type ‘const employee’

I know I am not supposed to modify any of the key values, and I don't, but I still need non-const acces sto modify other data in the container elements.

I know I have done this successfully elsewhere, I just can't see what would make this const.

The code below is derived from the original boost::multi_index example

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>


using boost::multi_index_container;
using namespace boost::multi_index;

struct employee
{
  int         id;
  int         tst;

  employee(int id_):id(id_), tst(0){}
};


struct id{};


typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<
      tag<id>,  BOOST_MULTI_INDEX_MEMBER(employee,int,id)> >
> employee_set;


void tst(employee_set& s)
{
  employee_set::index_iterator<id>::type it = s.get<id>().find(11);
  employee& eref = *it;

  eref.tst++;
}
like image 943
nhed Avatar asked Oct 26 '11 19:10

nhed


3 Answers

multi_index doesn't know that you're not going to change members which are the key values. That's why it's only implements const_iterator.

If you want to modify non-key member you can use modify function. If you're going to change key value you can use modify_key or replace member functions. You can get more info here.

like image 87
Kirill V. Lyadvinsky Avatar answered Nov 14 '22 22:11

Kirill V. Lyadvinsky


multi_index_container<T,...> iterators are immutable, regardless of whether typename T is a class type (U) or pointer type (U*). The maintainers chose to impose this constraint to safeguard against and emphasize the fact that modifying an element directly (i.e., through an iterator) could violate the integrity-constraints of any key-based (associative) indices of the container.

For example: An ordered_unique index would be completely invalidated if direct modification of an element (through an iterator) caused the index to no longer be ordered or unique, and the multi_index_container itself would have no knowledge of this change/invalidation (nor would the programmer).

Here is a verbatim explanation provided by the maintainers:

By design, index elements are immutable, i.e. iterators only grant const access to them, and only through the provided updating interface (replace, modify and modify_key) can the elements be modified. This restriction is set up so that the internal invariants of key-based indices are not broken (for instance, ascending order traversal in ordered indices), but induces important limitations in non key-based indices:

Circumventing Const-Correctness

Of course, there are always ways around const-correctness, the most obvious way being const_cast... but the spirit of C++ is that a programmer should be allowed to do anything he or she wants, even if it's potentially dangerous. C++ language/library designers simply do the best they can to discourage such uses. And hopefully, when programmers do decide to circumvent those safeguards, it's because they have good reason and know exactly what they're doing.

I hope this helps.

like image 22
etherice Avatar answered Nov 14 '22 21:11

etherice


From the MultiIndex docs on random access indices:

As usual in Boost.MultiIndex, elements of random access indices are immutable and can only be modified through member functions replace and modify. This precludes the usage of many mutating algorithms that are nonetheless applicable to std::vectors.

This applies to ordered indexes as well.

like image 33
Michael Goldshteyn Avatar answered Nov 14 '22 22:11

Michael Goldshteyn