Change/update value using std::map const_iterator - c++

I am just curious to know if I can change/update the map's value using const_iterator.
Below is the code snippet:
int main()
{
map <int, int> m;
m.insert(make_pair(1, 10));
map <int, int>::const_iterator itr = m.begin(); //The iterator is const_iterator
itr->second = 30;
cout << itr->second; //The value to be printed is 30, and not 10.
return 0;
}
Thank you in advance for sharing your ideas.

The whole point of const_iterator is that it cannot be used to modify the container. So no.

It is not possible to change an element through a const iterator. That's the most important distinction between a const iterator and non-const iterator.
std::map::begin returns a non-const iterator (assuming the object operand is non-const), so there is no need to use a const iterator in the first place.
However, if for some reason (not demonstrated in the example) you can only have a const iterator, but have non-const access to the container, then you can get a non-const iterator to the element pointed by the const iterator. This can be achieved by using following, which looks like a trick:
map <int, int>::iterator mutable_itr = m.erase(itr, itr);
It doesn't erase anything, because [itr, itr) is an empty range.

Can I change/update the map's value using const_iterator?
No!
It's called const iterator for a reason. As mentioned here, A const_iterator is an iterator that points to const value (like a const T* pointer); dereferencing it returns a reference to a constant value (const T&) and prevents modification of the referenced value: it enforces const-correctness.

The compiler should not allow modifying the contents of a map through a const_iterator. The line
itr->second = 30;
should be reported as an error. If your compiler allows that line, then it is not standards compliant. Perhaphs it allows that line to be compiled through flags that allow standards-incompatible behavior.
Using g++, I get the following error.
socc.cc: In function ‘int main()’:
socc.cc:12:19: error: assignment of member ‘std::pair<const int, int>::second’ in read-only object
itr->second = 30;

const iterator is supposed to prevent the user from changing/modiyfing the value by any means.

Related

Why I get the following error: "returning reference to temporary [-Werror=return-local-addr]"

given the following code:
#include <set>
using std::set;
class Pool {
set<int> s;
public:
typedef typename set<int>::iterator Iterator;
const Iterator& begin() const {
return s.begin(); //** error
}
};
Why I get the following error (I understand the meaning of the error, but, I don't understand why I get it in this case)?
returning reference to temporary [-Werror=return-local-addr]
How can I fix it?
The set<...>::begin function returns its iterator by value. Since you do not store that value anywhere, it's a temporary value, and you can't really have references to temporary values.
The simple solution is for your function to also return by (non-const) value.
const Iterator& begin() const {
return s.begin(); //** error
}
is illegal because the result of s.begin() is a temporary object and therefore you can't return a reference to it. Maybe if you look at this equivalent code it will be clearer?
const Iterator& begin() const {
Iterator it = s.begin();
return it; //** error
}
Returning a const reference to an iterator doesn't make much sense anyway as you wont be able to move the location that the iterator points to. You should always return iterators by value, this way the caller is free to make copies of the iterator and modify their location. For example if you could get your code to compile the following calling code would not work:
Pool p;
const Pool::Iterator& it = p.begin();
++it; //error it is constant
The user could fix their code by copying your returned reference into a new object:
Pool p;
Pool::Iterator it = p.begin();
++it; //no error
As your users wont be able to use the reference you can't return its better just to return by value:
const Iterator begin() const {
return s.begin();
}
Note that std::set is not like most other containers and doesn't allow modification of the values via its iterators: https://en.cppreference.com/w/cpp/container/set/begin
Because both iterator and const_iterator are constant iterators (and may in fact be the same type), it is not possible to mutate the elements of the container through an iterator returned by any of these member functions.

How get non-const iterator

std::map<char,int> dict;
...
auto pmax = dict.begin(); // here i get const iterator
Can I "explicitly indicate" that the value obtained is a non-constant type?
If your dict is not const, begin will return a std::map<char,int>::iterator. Now, the key is const, but the value is not.
auto should give you a std::map<char,int>::iterator; do you have evidence to the contrary?
Looking at your code, you are basically implementing std::max_element. So you could rewrite your last output line to:
std::cout << std::max_element(begin(dict), end(dict),
[](decltype(*begin(dict)) a, decltype(*begin(dict)) b) {
return a.second < b.second;
})->first << std::endl;
Admittedly, the decltype(*begin(dict)) is ugly, which hopefully will be remedied by generic lambdas in C++1y.
The point is, that regardless of whether you have a map::iterator or map::const_iterator when you dereference it, the result will be a std::pair with a const key_type as first argument. So, even if you have two iterators it1, it2 to mutable data (e.g., acquired via map::begin()), you can not re-assign the complete pair that is referenced by those iterators. Thus, *it1 = *it2 won't work, because you are trying to overwrite the mapped_type and the const key_type.

How does begin() know which return type to return (const or non-const)?

This works perfectly :
list<int> l;
list<int>::const_iterator it;
it = l.begin();
list<int>::iterator it2;
it2 = l.begin();
What I don't get is how the list "knows" that it must return the iterator begin() version or the const_iterator begin() const one.
I'm trying to implement iterators for my container (a trie) and I'm running into this problem. Isn't C++ supposed not to handle differentiation by return type (except when using weird tricks)?
Here is some code and the compiler error I get :
My Trie<T> is a templated trie that can contain any type. I have a Trie<int>::iter non-const iterator and a Trie<int>::const_iter const iterator. iter begin() and const_iter begin() const are declared (and defined) in the Trie class.
Trie<int> t;
Trie<int>::const_iter it;
it = t.begin();
Error :
../test/trie.cpp:181: error: no match for 'operator=' in 'it = Trie<T>::begin() [with T = int]()'
[..path..]/Trie.h:230: note: candidates are: Trie<int>::const_trie_iterator& Trie<int>::const_trie_iterator::operator=(const Trie<int>::const_trie_iterator&)
So, I believe the non-const version of begin is not used.
I contemplated creating an operator=(const Trie<T>::const_trie_iterator&) method for the non-const iterator but I don't see that in the STD lib and I would have to const_cast the iterator. What should I do?
In standard containers, a non-const iterator is implicitly convertible to a const_iterator. The type returned is based solely on the const-ness of the object/reference on which begin() was called, which in your case would be iterator, there is a conversion that allows the later assignment.
In particular in the 23.2.1 General Container Requirements, table 96, it says that X::iterator must be convertible to X::const_iterator.
list knows which kind of iterator to return because there are two begin methods defined, one for when the list is const, and one for when it isn't. The declarations might look something like this:
template<class T>
class list {
public:
iterator<T> begin();
const_iterator<T> begin() const;
}
In the following example, the first, non-const iterator would be returned, because the list isn't const:
void doSomething(list<int> &myList) {
iterator<int> i = myList.begin();
...
}
In the next example, the list is declared as const, so the second version of begin that returns a const_iterator would be used instead:
void doSomethingElse(const list<int> &myList) {
const_iterator<int> i = myList.begin();
....
}
Of course, an iterator can always be cast to a const_iterator, so you could declare i to be a const_iterator in either example, but if you try to declare i to be an iterator in the second example you'll get an error since a const_iterator can not be implicitly cast as an iterator.

Overload operator-> for STL iterator

I'm writing my own implementation of the C++ STL map container. Now I'm trying to implement the iterator. It should allow you to do something like iter->first and iter->second that returns the key/value respectively and where iter is an object not a pointer. I'm wondering how I should overload this? It's a bit confusing because I'm not sure what the return type should be; it has to be an object with members first/second I suppose. Is it typical to return a reference to a wrapper/interface object or something like that?
If you really mean the C++ standard library, then the value_type of a map is a pair. A pair has members first and second. Dereferencing an iterator in a map gives you a pair.
Yes, you'll need a proxy to hold the relevant reference.
As for the type: standard-library iterators typically dereference to something of type value_type. For map<K,V>, the value type is std::pair<K, V> (or rather, pair<key_type, mapped_type>), which is where you get the first/second interface from.
(One of Stephan Lavavej's lectures explains how the MSVC++ implementation uses the same underlying data structure for set and map; the only difference is that set::value_type equals set::key_type, while map::value_type is pair<key_type, mapped_type>. That way you can tell the two apart with a simple trait check, but the iterator interface is virtually identical.)
The value_type of a standard map is std::pair<const KeyType, MappedType>.
To achieve normal pointer semantics, operator* returns a reference, whereas operator-> returns a pointer.
//minimal example
#include <utility>
#include <cstdio>
struct It
{
std::pair<const int, int> pair;
std::pair<const int, int>* operator->() { return &pair; }
std::pair<const int, int>& operator*() { return pair; }
};
int main()
{
It it = {std::make_pair(10, 20) };
(*it).second = 30;
std::printf("%d %d\n", it->first, it->second);
}
std::map<K,V>::iterator iterates over objects of type std::pair<K,V>.
The answer to your question is yes. You should return a proxy-object or a reference to a proxy-object to get that behavior.

What is the difference between const_iterator and non-const iterator in the C++ STL?

What is the difference between a const_iterator and an iterator and where would you use one over the other?
const_iterators don't allow you to change the values that they point to, regular iterators do.
As with all things in C++, always prefer const, unless there's a good reason to use regular iterators (i.e. you want to use the fact that they're not const to change the pointed-to value).
They should pretty much be self-explanatory. If iterator points to an element of type T, then const_iterator points to an element of type 'const T'.
It's basically equivalent to the pointer types:
T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator
A const iterator always points to the same element, so the iterator itself is const. But the element it points to does not have to be const, so the element it points to can be changed.
A const_iterator is an iterator that points to a const element, so while the iterator itself can be updated (incremented or decremented, for example), the element it points to can not be changed.
Unfortunaty, a lot of the methods for the STL containers takes iterators instead of const_iterators as parameters. So if you have a const_iterator, you can't say "insert an element before the element that this iterator points to" (saying such a thing is not conceptually a const violation, in my opinion). If you want do that anyway, you have to convert it to a non-const iterator using std::advance() or boost::next(). Eg. boost::next(container.begin(), std::distance(container.begin(), the_const_iterator_we_want_to_unconst)). If container is a std::list, then the running time for that call will be O(n).
So the universal rule to add const wherever it is "logical" to do so, is less universal when it comes to STL containers.
However, boost containers take const_iterators (eg. boost::unordered_map::erase()). So when you use boost containers you can be "const agressive". By the way, do anyone know if or when the STL containers will be fixed?
Minimal runnable examples
Non-const iterators allow you to modify what they point to:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);
Const iterators don't:
const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
As shown above, v.begin() is const overloaded, and returns either iterator or const_iterator depending on the const-ness of the container variable:
How does begin() know which return type to return (const or non-const)?
how does overloading of const and non-const functions work?
A common case where const_iterator pops up is when this is used inside a const method:
class C {
public:
std::vector<int> v;
void f() const {
std::vector<int>::const_iterator it = this->v.begin();
}
void g(std::vector<int>::const_iterator& it) {}
};
const makes this const, which makes this->v const.
You can usually forget about it with auto, but if you starting passing those iterators around, you will need to think about them for the method signatures.
Much like const and non-const, you can convert easily from non-const to const, but not the other way around:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
// non-const to const.
std::vector<int>::const_iterator cit = it;
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
// Compile time error: no conversion from const to no-const.
//it = ci1;
Which one to use: analogous to const int vs int: prefer const iterators whenever you can use them (when you don't need to modify the container with them), to better document your intention of reading without modifying.
Use const_iterator whenever you can, use iterator when you have no other choice.
(as others have said) const_iterator doesn't allow you modify the elements to which it points, this is useful inside of const class methods. It also allows you to express your intent.
ok Let me explain it with very simple example first without using constant iterator
consider we have collection of random integers collection "randomData"
for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;
As can be seen for writing/editing data inside collection normal iterator is used but for reading purpose constant iterator has been used . If you try using constant iterator in first for loop you will get error . As a thumb rule use constant iterator to read data inside collection .