In C++, what is the type of a std::map<>::iterator?
We know that an object it of type std::map<A,B>::iterator has an overloaded operator -> which returns a std::pair<A,B>*, and that the std::pair<> has a first and second member.
But, what do these two members correspond to, and why do we have to access the value stored in the map as it->second?
I'm sure you know that a std::vector<X> stores a whole bunch of X objects, right? But if you have a std::map<X, Y>, what it actually stores is a whole bunch of std::pair<const X, Y>s. That's exactly what a map is - it pairs together the keys and the associated values.
When you iterate over a std::map, you're iterating over all of these std::pairs. When you dereference one of these iterators, you get a std::pair containing the key and its associated value.
std::map<std::string, int> m = /* fill it */;
auto it = m.begin();
Here, if you now do *it, you will get the the std::pair for the first element in the map.
Now the type std::pair gives you access to its elements through two members: first and second. So if you have a std::pair<X, Y> called p, p.first is an X object and p.second is a Y object.
So now you know that dereferencing a std::map iterator gives you a std::pair, you can then access its elements with first and second. For example, (*it).first will give you the key and (*it).second will give you the value. These are equivalent to it->first and it->second.
The type of the elements of an std::map (which is also the type of an expression obtained by dereferencing an iterator of that map) whose key is K and value is V is std::pair<const K, V> - the key is const to prevent you from interfering with the internal sorting of map values.
std::pair<> has two members named first and second (see here), with quite an intuitive meaning. Thus, given an iterator i to a certain map, the expression:
i->first
Which is equivalent to:
(*i).first
Refers to the first (const) element of the pair object pointed to by the iterator - i.e. it refers to a key in the map. Instead, the expression:
i->second
Which is equivalent to:
(*i).second
Refers to the second element of the pair - i.e. to the corresponding value in the map.
Used for map and unordered map.
map stores the key-value pair where i->first is for key and i->second is reflecting value.
#include<bits/stdc++.h>
#define long long int
using namespace std;
int32_t main(){
map<int,int> m;
m.insert({1,2});
m.insert({2,4});
for(auto i:m){
cout<<"key - "<<i.first<<" "<<" Value - "<<i.second<<endl;
}
}
Related
What does the value_comp do, like does it check the first value equal to the second?
and * operator before the iterator?
map<char, char> m =
{
{ 'a', 'A' },
{ 'b', 'B' },
{ 'c', 'C' },
{ 'd', 'D' },
{ 'e', 'E' },
};
auto last = *m.rbegin(); // How does "*" affect the iterator returned
auto i = m.begin();
do
{
cout << i->first
<< " = "
<< i->second
<< endl;
} while (m.value_comp()(*i++, last));
// Does the value_comp compares *i++ and the last value or iterator
How does "*" affect the iterator returned?
It dereferences the iterator and gives the pointee. Meaning the last has type
std::pair<const char, char>
or
decltype(m)::value_type // or std::map<char, char>::value_type
If you would have just auto last = m.rbegin(); (i.e. without dereferencing), you would get the iterator there, meaning the last would have deduced to std::map<char, char>::iterator.
Does the value_comp compares *i++ and the last value or iterator?
std::map::value_comp returns:
a function object that compares objects of type
std::map::value_type (key-value pairs) by using key_comp to
compare the first components of the pairs.
Therefore, it compares the values of the map, not the iterator pointing to the key-value pairs.
So, it looks like you have basically two questions:
auto last = m.rbegin(); //How does "" affect the iterator returned
The "*" operator returns the reference of value_type which for your map would be std::pair<const char, char>.
Does the value_comp compares *i++ and the last value or iterator
value_comp() returns the function that compares keys in objects of type value_type. So, in your code it compares keys i->first and last.first. It returns true if the first key is less
How does "*" affect the iterator returned?
The dereference (*) means that the value stored in last is not the iterator, but a copy of what's stored at that position in the map, which incidentally is the last node of the map. It works much in the same way as dereferencing a pointer, you get the value pointed by it, not the pointer itself.
Does the value_comp compares *i++ and the last value or iterator?
Yes it does. It compares the two keys, the one "pointed by" i and the one stored in last. The method will return true as long as the first argument key is at a lower position on the map than the key of the second argument.
In your example, when the keys match the cycle ends.
As per the description of the method:
It returns a comparison object that can be used to compare two elements to get whether the key of the first one goes before the second.
The comparison object returned is an object of the member type map::value_compare, which is a nested class that uses the internal comparison object to generate the appropriate comparison functional class.
Side note:
It may be clear that *i++ also returns the content of i, and the iterator i is being incremented at each iteration, but this expression can cause confusion, you could use brackets to avoid it, like so *(i++).
In an unordered_map, when we use the [] operator to get the value for a key, then if the key is not present, it performs an insertion. I want to understand how it is implemented.
std::unordered_map<int, int> x;
x[1] = 10;
In the above statement, how is the rvalue 10 accessed and used to perform an insertion ?
Very straightforward.
Operator [] for std::map and std::unordered_map looks for an element with a given key. If the element is present, it returns the reference to the second member of it's pair. If it is not present, it creates a new pair, with a key (first) equal to given key and value-initialized second, inserts this element and returns reference to the second.
Then operator= works normally.
I was puzzled when I received an error that suggested I use a . operator to access the value in a pair from a map because when I changed it in two places in code I received a new error that suggested that I use a -> in the second place in code. I listened to the naggy compiler. Why did I need to?
Here's what I was doing:
In a range based for loop, I want the value from the key-value pair which could be exampled:
std::map<std::string, aclass> mapthings;
...
for (auto& it : mapthings) {
fout << it.second.stringify();
}
I'm also using the same mapthings but using the find() function:
return (mapthings.find(name))->second;
Because find return mapthings::iterator,which stores a pointer to mapthings::value_type.
While auto& it is a object of mapthings::value_type.
In the first example range-based for loop gives object directly. So you can use ..
In the second example find gives you iterator to the object. That compels you to use -> because you need object itself.
In this code snippet
std::map<std::string, aclass> mapthings;
...
for (auto& it : mapthings) {
fout << it.second.stringify();
}
it is of type value_type of std::map<std::string, aclass> that corresponds to type
std::pair<const std::string, aclass>
So to access members of an object of this type you have to use operator .
In this code snippet
return (mapthings.find(name))->second;
method find returns iterator that points to the target record of the map or to iterator returned by end(). Iterators are like pointers. So you need to use operator -> to access members of the pointed object.
Take into account that you could write simpler
return mapthings.find( name )->second;
or
return ( *mapthings.find( name ) ).second;
When you use this code, you get a reference to a std::pair containing your data
for (auto& it : mapthings) {
fout << it.second.stringify();
}
In the second example, find returns an iterator which needs to be dereferenced to be read
In the first one, you get a std::map::value_type&, which is defined to be std::pair<const Key, T>. This method is best for accessing all the members of the map.
In the second one, you get a std::map::iterator. std::map::iterator has operator-> overloaded, which returns a std::map::value_type*. This method is best for find, upper_bound, lower_bound, etc. since you are performing queries on the map. The map might not hold a member corresponding to the key used in the query.
When using a boost unordered_map, if I do map[key] will the type returned be a reference to the value V or a copy of the value?
Thanks
It is a reference, otherwise this wouldn't work:
map[key] = value;
According to the documentation, it would appear to be a reference.
mapped_type& operator[](key_type const& k);
From Boost documentation :
mapped_type& operator[](key_type const& k);
Effects:
If the container does not already contain an elements with a key equivalent to k, inserts the value std::pair(k, mapped_type())
Returns:
A reference to x.second where x is the element already in the container, or the newly inserted element with a key equivalent to k
Throws:
If an exception is thrown by an operation other than a call to hasher the function has no effect.
Notes:
Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.
Pointers and references to elements are never invalidated.
The accessor operator [] always returns a reference. The map element will be created if it doesn't exist. So to avoid repeated searches, the following pattern is useful:
std::unordered_map<K, T> m = populate_me();
T & value = m[key]; // created if non-existent
// use "value" rather than repeated "m[key]"
I add the constructor and two functions to the class of my previous linked question C++ iterate through a template Map and I need help at this points:
What do you reckon this constructor does?
Adding one value at the beginning of map?
I see though in the respective key only an address as value after initializing in main. What is wrong?
The operator [] is supposed to get the values for a specific key. However I cannot use it so as to get the elements of the map in the output. Any hint?
template<class K, class V>
class template_map{
public:
template_map( V const& val) {
m_map.insert(my_map.begin(),std::make_pair(std::numeric_limits<K>::min(),val));
};
typedef typename std::map<K,V> TMap;
TMap my_map;
typedef typename TMap::const_iterator const_iterator;
const_iterator begin() const { return my_map.begin(); }
const_iterator end() const { return my_map.end(); }
V const& operator[]( K const& key ) const {
return ( --my_map.upper_bound(key) )->second;
}
...
};
int main()
{
interval_map<int,int> Map1 (10);
//Show the elements of the map?
}
Consider also that it should be a function that inserts values to the map.
What do you reckon this constructor does? Adding one value at the beginning of map?
It initialises the map so that map[x] == v for any x. The map associates intervals with values, internally storing a normal map keyed by the start of each interval; it's initialised so that the entire range of the key type maps to the initial value.
I see though in the respective key only an address as value after initializing in main. What is wrong? The operator [] is supposed to get the values for a specific key. However I cannot use it so as to get the elements of the map in the output. Any hint?
I've no idea what you're asking there. If you try, for example, cout << Map1[42] << '\n';, then your program should output 10, since that is the initial value assigned to the entire range of integers.
Consider also that it should be a function that inserts values to the map.
Since the internal map is publicly exposed, you can add a new interval to the map with
Map1.my_map.insert(std::make_pair(interval_start, value));
It might be more polite to make my_map private, and provide an insert() function to do that. You could also add a non-const overload of operator[] that inserts a new range and returns a reference to its value, something like
V & operator[](K const & key) {
V const & old_value = (--my_map.upper_bound(key))->second;
return *my_map.insert(std::make_pair(key, old_value)).first;
}
although this might not be a great idea, as you'd have to be careful that you don't accidentally insert many ranges when you only want to read the values.
My problem is how to iterate through the map to get all its elements and print them in main. It shows me an address with a value of the object initialization.
Remembering that an iterator over a map refers to a key/value pair (of type std::pair<K,V>), you should be able to iterator over the map like this:
for (auto it = Map1.begin(); it != Map1.end(); ++it) {
std::cout << it->first << " maps to " << it->second << '\n';
}
(in C++03, you'll need to write template_map<int,int>::const_iterator rather than auto).
What do you reckon this constructor does? Adding one value at the
beginning of map? I see though in the respective key only an address
as value after initializing in main. What is wrong?
It adds this one value in to the map. The iterator argument is only a hint: if the new item is to be inserted right after this position, the operation can be completed faster. Otherwise the map will need to find the right place to insert the new value as usual.
The operator [] is supposed to get the values for a specific key.
However I cannot use it so as to get the elements of the map in the
output. Any hint?
upper_bound returns iterator to the first key-value pair, where key is greater than the argument. --upper_bound therefore returns an iterator to the item, whose key is either equal or less than the queried key. If upper_bound returned map.begin(), because all keys are greater than the query, decrementing it is undefined behavior.
What you need here is the find member function. You also need to deal with the case the key is not found (map.end() is returned), e.g by throwing an exception.
Alternatively you may implement your operator[] in terms of map::operator[]. This means that the function can't be const, because map inserts a new default value if the key is not found.
The iterator in map::insert() is just a hint; essentially it doesn't mean anything in terms of the semantics of the program.
Your code inserts the value passed through the constructor argument together with the key numeric_limits<K>::min(), i.e. the smallest possible value for the given key type. This will only complile if numeric_limits is specialized for the type K.
Also note that if the key already exists, the corresponding mapped value will not be overwritten, so a corresponding insert function would be of very limited use.