Is there any way to hook insertion and deletion operations for the std containers?

Viewed 149

Let's say, we are going to subclass the std::map and we need to catch all insertions and deletions to/from the container. For example, in order to save some application-specific information about the keys present in the container.

What's the easiest way to do this, if at all possible?

Probably, the most obvious way to do this is to override all methods and operators that perform the insertion and deletion. But I think, something may be easily lost sight of on this way, isn't it?

2 Answers

There is no way to do that in the general case. Inheritance is not a good idea because std::map is not polymorphic and no virtual dispatch will happen when you use a pointer to a map. You might as well use a simple wrapper class at that point and save yourself a lot of hassle:

#include <iostream>
#include <map>

template <class Key, class Value> 
struct Map {
private:
  std::map<Key, Value> _data;

public:
  template <class Y, class T> 
  void insert(Y &&key, T &&val) {

    std::cout << "[" << key << "] = " << val << "\n";

    _data.insert_or_assign(std::forward<Y>(key), std::forward<T>(val));
  }

  void remove(Key const &key) {
    auto const it = _data.find(key);

    if (it == _data.end())
      return;

    std::cout << "[" << key << "] -> removed\n";

    _data.erase(it);
  }

  Value *get(Key const &key) {
    auto const it = _data.find(key);

    if (it == _data.end())
      return nullptr;

    return &it->second;
  }
};

int main() {
  Map<int, char const *> map;
  map.insert(10, "hello");
  map.insert(1, "world");
  map.remove(1);
  map.remove(10);
  map.remove(999);
}

Short answer: No

C++ standard library data structures were not designed to support this use case. You may subclass and try to override but this will not work as you'd expect. In fact you'll get an error at compile time if you do it properly with the help of the keyword override. The problem is that std::map methods are not virtual so they don't support so called late binding. Functions that work with references and pointers to std::map will keep using std::map methods even in the case of passing instances of your std::map subclass.

Your only option is to create a completely new class your_map with a subset of requred methods of std::map and to delegate the job to an inner instance of std::map as shown in Ayxan Haqverdili's answer. Unfortunately this solution requires you to change the signature of functions working with your code replacing std::map & arguments with your_map & which may not be always possible.

Related