Search code examples
c++stlconstantsstd-pair

How does this const reference for std::pair work?


Consider std::map class in STL:

template < class Key,                                     // map::key_type
           class T,                                       // map::mapped_type
           class Compare = less<Key>,                     // map::key_compare
           class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
           > class map;

Iterators to std::map return objects of type

std::pair<const key_type, T>

The important thing to note here is that the first member of the pair is const. This means that, the following reference assignment is invalid.

std::pair<key_type, T>& reference = *map_iterator;        // Incorrect, key_type is not const

std::pair<const key_type, T>& reference = *map_iterator;  // Correct

However, the following expression is valid :

const std::pair<key_type, T>& reference = *map_iterator;  // Correct, const reference is okay

So, by some mechanism, objects of type std::pair<const key_type, T> can be referred by reference of type const std::pair<key_type, T>. This is logically desirable (since constness of std::pair implied constness of both the members first and second, which is compatible with std::pair<const key_type, T>).

However, I am interested in knowing what C++ implementation mechanism makes this compatibility possible. I'm sure there ways of implementing std::pair where the two reference types above would not be compatible.


Solution

  • When you do

    const std::pair<key_type, T>& reference = *map_iterator;
    

    *map_iterator returns a std::pair<const key_type, T>&. You then copy initialize a std::pair<key_type, T> from that, and then you bind reference to that temporary variable. Because you have a reference to const, this will extend the lifetime of that temporary to be the lifetime of the reference and you now have an element that is a copy of the one from the map. Basically you've done

    std::pair<key_type, T> copy = *map_iterator; 
    

    The above conversion sequence works because you are allowed up to one user defined conversion in initialization and the compiler will try to do that to give you a valid initialization.