Search code examples
c++c++11for-loopstlauto

Why can I use a reference to non-const with 'auto' but not with 'std::pair' in range-based for-loop through 'std::unordered_map'?


Using C++14 (should also affect C++11) I'm confused about auto in a range-based for-loop through an std::unordered_map, in contrast of using the exact type like std::pair<int, int> in the code below.

More specifically I have some (related) questions concerning the example:

  • Question 0: (loop 0) Why is std::pair<int, int> & not allowed but auto & in loop 1 is?
  • Question 1: (loop 1) Why/How/When does auto differ from the exact type (like std::pair<int, int> in loop 0)?
  • Question 2: (loop 2) Why are this different pointers than in loop 3?
  • Question 3: (loop 3) Why are this the same pointers for all map entries?

  • Question 4 (continues 0 and 1): I know that the range-based for-loop uses an iterator. But why can I iterate through an std::unordered_map with reference to non-const using auto (loop 1), but not when using std::pair (loop 0)?


#include<iostream>
#include <unordered_map>
#include <utility>

int main()
{
    std::unordered_map<int, int> map;
    map[3] = 333;
    map[2] = 222;
    map[1] = 111;

    // loop 0
    // QUESTION 0: Why is `std::pair<int, int> &` not allowed but `auto &` in loop 1 is?
    // for(std::pair<int, int> & pair : map)
    //     pair.second++;

    // loop 1
    for(auto & pair : map)
        // QUESTION 1: Why/How/When does `auto` differ from the exact type (like `std::pair<int, int>` in loop 0)?
        pair.second++;

    // loop 2
    for(auto const & pair : map)
        // QUESTION 2: Why are this different pointers than in loop 3?
        std::cout << pair.first << " (" << &pair.first << ") : " << pair.second << " (" << &pair.second << ")" << std::endl;

    // loop 3
    for(std::pair<int, int> const & pair : map)
        // QUESTION 3: Why are this the same pointers for all map entries?
        std::cout << pair.first << " (" << &pair.first << ") : " << pair.second << " (" << &pair.second << ")" << std::endl;
    return 0;
}

You can run the code here: https://www.onlinegdb.com/rkBkKDatf


Solution

  • The value type of a std::unordered_map is std::pair<const Key, T>. See the documentation at cppreference.com.

    Hence, you cannot use std::pair<int, int>& as the type to iterate over the contents of such an object.


    That explains why

    for(std::pair<int, int> & pair : map) { ... }
    

    does not work.


    The following works

    for(auto const & pair : map)
    

    since the type is deduced for you by the compiler.


    The following works

    for(std::pair<int, int> const & pair : map)
    

    since pair is bound to a temporary object of type std::pair<int, int> constructed from a std::pair<const int, int>.