Search code examples
c++containersc++14

Why is heterogenous std::*map lookup opt-in in C++?


To support heterogeneous key lookup for a std::map one has to be a bit more verbose that in the olden days: (taken from the question on how to do it)

int main()
{
    {
        puts("The C++11 way makes a copy...");
        std::map<std_string, int> m;
        auto it = m.find("Olaf");
    }
    {
        puts("The C++14 way doesn't...");
        std::map<std_string, int, std::less<>> m;
        auto it = m.find("Olaf");
    }
}

Also see: https://www.cppstories.com/2021/heterogeneous-access-cpp20/ for an explanation.

From this link and from the linked question (and also from N3657) there a a few scattered reasons given as to why this is opt-in. Since I had to do quite some scrolling and I didn't find a succinct summary of the reasons, I would like to put together a summary here that every junior dev understands, as to

Why stupid C++ makes me write that extra std::less<> ??! Why doesn't it just allow transparent heterogeneous comparison with the default type we always used?

;-)


Solution

  • If std::map would simple support this silently, that is if std::map<KeyT, ValT> would support find(LookupT) for "any compatible" type then:

    implicitly supporting heterogeneous lookup can be dangerous, as the relationship between values might not be maintained after conversions. For example, 1.0 < 1.1, but static_cast<int>(1.0) == static_cast<int>(1.1). Thus, using a double to look up a value in a std::set<int> could lead to incorrect results.

    • Performance(!) - old code with types that do not support heterogeneous comparison would start to run slower: dyp wrote:

    Consider there exists for some type stupid_string only a converting constructor from char const* but not a comparison operator operator<(stupid_string const&, char const*), only operator<(stupid_string const&, stupid_string const&). Since std::less<> ((would)) forward to the comparison operator, each comparison will create a new stupid_string.

    ((instead of just creating the extra comparison object once on the call site of std::find))