Re: Casting STL's const_iterator to iterator
In article <49f62a9e$0$92442$dbd4d001@news.wanadoo.nl>,
Nick Overdijk <nick@dotsimplicity.net> wrote:
How would I cast a const_iterator to a normal iterator? For example:
template <typename T1, typename T2>
typename multimap<T1, T2>::iterator &get_indexed_it(const
multimap<T1, T2> &container, const T1 &key, size_t index = 0) const
throw (out_of_range) {
size_t count = container.count(key);
if (count != 0 && count-1 >= index) {
typename multimap<T1, T2>::const_iterator it =
container.find(key);
while (index--) it++;
return const_cast<typename multimap<T1, T2>::iterator&>
(it);
} else {
throw out_of_range((string)"get_indexed_it->"+"Element
" + key + "doesn't exist!");
}
}
MinGW GCC on compiling says:
D:\MyDocs\Code\SSSParser\sss.hpp|155|error: invalid const_cast from type
`std::_Rb_tree_const_iterator<std::pair<const std::string, sss::node> >'
to type `std::_Rb_tree_iterator<std::pair<const std::string, sss::node> >&'|
I'm guessing this is because const_iterator isn't the same as const
iterator, how else would I accomplish this?
The two obvious solutions for solving your problem:
template <typename T1, typename T2>
typename multimap<T1, T2>::const_iterator get_indexed_it(
const multimap<T1, T2>& container,
const T1& key,
size_t index = 0) throw (out_of_range)
{
size_t count = container.count(key);
if (count != 0 && count - 1 >= index) {
typename multimap<T1, T2>::const_iterator it = container.find(key);
while (index--)
it++;
return it;
} else {
throw out_of_range((string)"get_indexed_it->" + "Element"
+ key + "doesn't exist!");
}
}
or
template <typename T1, typename T2>
typename multimap<T1, T2>::iterator get_indexed_it(
multimap<T1, T2>& container,
const T1& key,
size_t index = 0) throw (out_of_range)
{
size_t count = container.count(key);
if (count != 0 && count - 1 >= index) {
typename multimap<T1, T2>::iterator it = container.find(key);
while (index--)
it++;
return it;
} else {
throw out_of_range((string)"get_indexed_it->" + "Element"
+ key + "doesn't exist!");
}
}
A couple of comments about your code (and the two 'solutions' above.)
a) Returning a reference (either a reference to const_iterator or a
reference to iterator,) doesn't make much sense to me; iterators are
light weight and designed to be passed around by value.
b) If T1 is not a string (or some user defined type designed to work as
a string,) your code won't compile. Why not just make it a string?
Writing it more like this:
template <typename Container, typename T>
typename Container::iterator& get_indexed_it(Container& container, const
T& key, size_t index = 0) throw (out_of_range)
{
if (container.count(key) <= index )
throw out_of_range("get_indexed_it(Container&, const T&, size_t)");
typename Container::iterator result = container.find(key);
advance(result, index);
return result;
}
a) Allows it to be used with maps, sets and multisets, as well as
multimaps.
b) Allows it to work even if 'key' is not a string like object.
c) (arguably) better expresses the intent of the code.
Better still would be to find some way to cleanly write the function
without having to do the search twice (once for count, and once for
find):
template <typename Container, typename T>
typename Container::iterator& get_indexed_it(
Container& container,
const T& key,
size_t index = 0) throw (out_of_range)
{
pair<typename Container::iterator, typename Container::iterator>
range = container.equal_range(key);
if (distance(range.first, range.second) <= index)
throw out_of_range("get_indexed_it(Container&, const T&, size_t)");
advance(range.first, index);
return range.first;
}
Just some thoughts.