Image for post

std::map and its siblings(std::multimapstd::unordered_map/multimap) used to be my favourite containers when I was doing competitive programming. In fact, I still like them(though using less frequently nowadays). And with Modern C++, we now have more reasons to use std::map. That’s why I have decided to address this topic by writing an article summarizing these new features. So, without much gibberish, let’s dive-in directly.

/!: This article has been originally published on my blog. If you are interested in receiving my latest articles, please sign up to my newsletter.

std::map::contains(C++20)

  • std::map::contains member function is a good step towards code expressiveness. And I am also tire of writing :
if (auto search = freq_of.find(2); search != freq_of.end()) {
    cout << "Found" << endl;
}
// Where assume, freq_of = map<uint32_t, uint32_t>{{3, 1}, {1, 1}, {2, 1}};
  • Rather, from C++20, you can write:
if (freq_of.contains(2)) {
    cout << "Found" << endl;
}

The code we write is written first for human consumption & only secondarily for the computer to understand.

- John Sonmez

std::map::try_emplace(C++17)

  • While inserting into the map, we have 2 different possibilities:
  1. The key doesn’t exist yet. Create a fresh key-value pair.
  2. The key does exist already. Take the existing item and modify it.
  • A typical approach to insert an element in std::map is by using operator[ ]std::map::insert or std::map::emplace . But, in all of these cases, we have to bear the cost of default/specialized constructor or assignment call. And the worst part is if an item already exists, we have to drop the freshly created item.
int main() {
    vector v{3, 4, 5, 8, 7, 3, 5, 2, 4};
    map<uint32_t, uint32_t> freq_of;

    for (const auto &n : v) {
        if (const auto &[it, inserted] = freq_of.emplace(n, 1); !inserted) {
            it->second++;  // Exists already
        }
    }
    assert(freq_of[3] == 2);
    return EXIT_SUCCESS;
}
  • Instead:
if (const auto &[it, inserted] = freq_of.try_emplace(n, 1); !inserted) {
    it->second++;
}
  • But, since C++17, there is this std::map::try_emplace method that creates items only if the key doesn’t exist yet. This boosts the performance in case objects of that type are expensive to create.
  • Although the above example hasn’t showcased the expensive to create items. But, yes! whenever you encounter such a situation, must be known how to handle it with std::map::try_emplace.

std::map::insert_or_assign(C++17)

  • When you have to insert element anyhow. For the sake of convenience, you use std::map::operator[ ]. Which is OK( and dangerous)! Unless you have any constraint on insertion or assignment.
  • For example, while counting the frequency of elements with the added constraint that when an element is repeated(i.e. assigned) you have to remove all the element lesser than the current one.
  • In such a situation, std::map::operator[ ] isn’t feasible. Rather, **_std::map::insert_or_assign_**_ is more appropriate and returns more information than _[**_std::map::operator[ ]_**](https://dev.tostd::map::operator%5B%20%5D/). It also does not require default-constructibility of the mapped type. Consider the following example for the same.
int main() {
    vector v{8, 3, 9, 5, 8};
    map<uint32_t, uint32_t> freq_of;

    for (auto &&n : v) {
        const auto &[it, is_inserted] = freq_of.insert_or_assign(n, 1);
        if (!is_inserted) { // remove all lesser element then current one if repeated
            freq_of.erase(begin(freq_of), it);
        }
    }
    assert((freq_of == decltype(freq_of){
                           {8, 1},
                           {9, 1},
                       }));
    return EXIT_SUCCESS;
}

#programming #coding #cpp11 #cpp #computer-science

Using std::map Wisely With Modern C++
2.95 GEEK