2
votes

I have the below snippet of code (which you can run here: http://coliru.stacked-crooked.com/a/2f62134b5c125051)

#include <iostream>
#include <set>
#include <map>

int main() 
{
    std::set<std::pair<const int, const int>> const mySet{{0,0}}; // value_type = std::pair<const int, const int>
    for (std::set<std::pair<const int, const int>>::iterator it  = mySet.cbegin(); it != mySet.cend(); ++it)
    {
        std::cout<<"set it = " << it->first << " " << it->second << std::endl;
    }

    std::map<const int, const int> const myMap{{0,0}}; // value_type = std::pair<const int, const int>
    for (std::map<const int, const int>::iterator it  = myMap.cbegin(); it != myMap.cend(); ++it)
    {
        std::cout<<"map it = " << it->first << " " << it->second << std::endl;
    }   
}

Can someone please explain me why for std::set the below does not throw any error:

std::set<std::pair<const int, const int>>::iterator it  = mySet.cbegin();

while for std::map the below throws error (no known conversion from _Rb_tree_const_iterator<std::pair<const int, const int> > to _Rb_tree_iterator<std::pair<const int, const int> >) as expected:

std::map<const int, const int>::iterator it  = myMap.cbegin();

How does it work for std::set? Shouldn't assigning a const_iterator to an iterator always throw an error?

3
If you put const in front of std::map or std::set, I don't think you will be able to do what you're doing. You'll get a compiler error. - aalimian
Also, your const doesn't apply directly to the elements of your vector or your set, it applies to the elements in your std::pair, so even with a non-const iterator of your vector you still won't be able to change the values of the pairs. - aalimian

3 Answers

4
votes

In practice std::set<T>::iterator is the same as std::set<T>::const_iterator because std::set's elements are immutable. It does not have a mutable iterator type.

This is not the case for std::map which is why you observe different behavior.

3
votes

From the C++ 17 Standard (26.2.6 Associative containers)

6 iterator of an associative container is of the bidirectional iterator category. For associative containers where the value type is the same as the key type, both iterator and const_iterator are constant iterators. It is unspecified whether or not iterator and const_iterator are the same type.

2
votes

You cannot modify a sets elements. Keys must be const to ensure the invariant promised by the set: elements are sorted and unique. A maps elements are sorted too, but you can modify the mapped value of the elements (keys are const as well). Elements of a std::map<A,B> are std::pair<const A,B>.

On cppreference you can read that member aliases for iterators of std::set are

iterator Constant LegacyBidirectionalIterator

const_iterator Constant LegacyBidirectionalIterator

They are both const iterators.

On the other hand for a std::map they are:

iterator LegacyBidirectionalIterator

const_iterator Constant LegacyBidirectionalIterator

Assigning a const_iterator to a non-const one is wrong. Its like trying to cast const away by assigning a pointer to const to a pointer to non-const. It won't work because you cannot make something that cannot be modified modifiable. That would break const-correctness.