std::map Requirements for Keys (Design Decision)

后端 未结 5 1209
天涯浪人
天涯浪人 2021-02-13 18:24

When I make a std::map, what C++ expects from me is that my_data_type has its own operator<.

5条回答
  •  深忆病人
    2021-02-13 18:54

    operator== is inevitable for std::map::find()

    This is where you go badly wrong. map does not use operator== at all, it is not "inevitable". Two keys x and y are considered equivalent for the purposes of the map if !(x < y) && !(y < x).

    map doesn't know or care whether you've implemented operator==. Even if you have, it need not be the case that all equivalent keys in the order are equal according to operator==.

    The reason for all this is that wherever C++ relies on orders (sorting, maps, sets, binary searches), it bases everything it does on the well-understood mathematical concept of a "strict weak order", which is also defined in the standard. There's no particular need for operator==, and if you look at the code for these standard functions you won't very often see anything like if (!(x < y) && !(y < x)) that does both tests close together.

    Additionally, none of this is necessarily based on operator<. The default comparator for map is std::less, and that by default uses operator<. But if you've specialized std::less for KeyType then you needn't define operator<, and if you specify a different comparator for the map then it may or may not have anything to do with operator< or std::less. So where I've said x < y above, really it's cmp(x,y), where cmp is the strict weak order.

    This flexibility is another reason why not to drag operator== into it. Suppose KeyType is std::string, and you specify your own comparator that implements some kind of locale-specific, case-insensitive collation rules. If map used operator== some of the time, then that would completely ignore the fact that strings differing only by case should count as the same key (or in some languages: with other differences that are considered not to matter for collation purposes). So the equality comparison would also have to be configurable, but there would only be one "correct" answer that the programmer could provide. This isn't a good situation, you never want your API to offer something that looks like a point of customization but really isn't.

    Besides, the concept is that once you've ruled out the section of the tree that's less than the key you're searching for, and the section of the tree for which the key is less than it, what's left either is empty (no match found) or else has a key in it (match found). So, you've already used current < key then key < current, leaving no other option but equivalence. The situation is exactly:

    if (search_key < current_element)
        go_left();
    else if (current_element < search_key)
        go_right();
    else
        declare_equivalent();
    

    and what you're suggesting is:

    if (search_key < current_element)
        go_left();
    else if (current_element < search_key)
        go_right();
    else if (current_element == search_key)
        declare_equivalent();
    

    which is obviously not needed. In fact, it's your suggestion that's less efficient!

提交回复
热议问题