[multi_index] no non-const reference even for simple sorted container
Hi!
I tried to use multi_index_container as a drop in replacement for std::map and hoped that I could obtain non-const references to the values therein. Although returning a non-const reference might not be a problem for a multi_index_container with only 1 sorting criterium the lib and the compiler insist on const &. Workarounds beyond modify(...) available? Markus #include <boost/tuple/tuple.hpp> #include <boost/multi_index_container.hpp> #include <boost/multi_index/sequenced_index.hpp> #include <boost/multi_index/key_extractors.hpp> #include <boost/multi_index/ordered_index.hpp> #include <string> namespace bm = ::boost::multi_index; // http://lists.boost.org/boost-users/2005/12/16088.php template<typename Tuple,int N> struct tuple_member_extractor { typedef typename boost::tuples::element<N,Tuple>::type result_type; const result_type& operator()(const Tuple& t) const { return boost::tuples::get<N>(t); } }; typedef boost::tuple<std::string, std::string> element; typedef bm::multi_index_container< element, bm::indexed_by<bm::ordered_unique <tuple_member_extractor<element, 0> >
multi_t;
int main() { multi_t test; test.insert(boost::make_tuple("a", "aa")); // will not compile element & ref = test.find("a"); std::string & s = ref.get<1>(); }
Hello Markus, Markus Werle ha escrito:
Hi!
I tried to use multi_index_container as a drop in replacement for std::map and hoped that I could obtain non-const references to the values therein.
Although returning a non-const reference might not be a problem for a multi_index_container with only 1 sorting criterium the lib and the compiler insist on const &. Workarounds beyond modify(...) available?
(First of all, your statement element & ref = test.find("a"); is incorrect in that it's missing an indirection. I assume your intended code is element & ref = *test.find("a"); although it doesn't compile either because of the const & issue you complain about.) B.MI always returns const references to the elements so as to not allow the user to modify the keys on her own. This has nothing to do with whether the number of indices is one or more, so I don't quite get your reasoning. If we could get a non-const reference the following violation of the internal invariants of B.MI would pass at compile time: element & ref = test.find("a"); ref.get<0>()="b"; // boom So, in order to get non-const access to the second member of your tuple (as the first must be kept untouched since it's a key), you've got to somehow circumvent this protective measure. There are several alternatives. 1. Use const_cast to eliminate constness const element & ref = *test.find("a"); std::string & s = const_cast<std::string &>(ref.get<1>()); This is dangerous but safe if you know what you're doing --i.e. you don't touch keys. 2. You can have a drop-in replacement for std::map if only you use a special type of pair-like element instead of a tuple, the details are given at http://tinyurl.com/337bvp . 3. You can keep using boost::tuples as your element types and also gain non-const access to selected elements by taking advantage of a facility provided by Boost.Tuple called access traits, see "Traits classes for tuple element types" at http://boost.org/libs/tuple/doc/tuple_advanced_interface.html // object wrapper granting non-const access under const // conditions template<typename T> class mutable_tuple_element { public: mutable_tuple_element(const T& t):t(t){} operator T&()const{return t;} private: mutable T t; }; // plug mutable_tuple_element into Boost.Tuple access // traits system namespace boost{ namespace tuples{ template<typename T> class access_traits<mutable_tuple_element<T> > { typedef T& non_const_type; typedef T& const_type; typedef const T& parameter_type; }; } // namespace tuples } // namespace boost // define now your element type as follows: typedef boost::tuple< std::string, mutable_tuple_element<std::string> > element; This allows you then to write: const element & ref = *test.find("a"); std::string & s = ref.get<1>(); // OK but does not allow this const element & ref = *test.find("a"); std::string & s = ref.get<0>(); // compile error which is perfect, since we don't want to change key elements. Complete code snippet attached. Hope this helps, Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
participants (2)
-
Joaquín Mª López Muñoz
-
Markus Werle