Что требуется для использования пользовательской структуры в качестве ключа в std::map?

«Что требуется для использования пользовательской структуры в качестве ключа в std::map?» — вопрос из категории STL, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для использования пользовательского типа (например, структуры) в качестве ключа std::map<K, V> необходимо обеспечить для этого типа строгое слабое упорядочивание (strict weak ordering). На практике это означает, что должен быть определен способ сравнения двух ключей на «меньше».

Способ 1: Перегрузка оператора < внутри структуры (наиболее распространённый).

#include <map>

struct Point {
    int x, y;
    // Ключевые моменты: метод const, параметр const&
    bool operator<(const Point& other) const {
        // Лексикографическое сравнение (сначала по x, затем по y)
        if (x != other.x) return x < other.x;
        return y < other.y;
    }
};

int main() {
    std::map<Point, std::string> labelMap;
    labelMap[{1, 2}] = "Start";
    labelMap[{3, 4}] = "End";
    // map автоматически использует перегруженный operator<
    return 0;
}

Способ 2: Создание внешнего функционального объекта (компаратора) и передача его в map.

struct Point { int x, y; };

struct PointComparator {
    bool operator()(const Point& a, const Point& b) const {
        return std::tie(a.x, a.y) < std::tie(b.x, b.y);
    }
};

int main() {
    // Явно указываем компаратор как третий шаблонный параметр
    std::map<Point, std::string, PointComparator> mapWithCustomComparator;
}

Важно: Если оператор сравнения не обеспечивает строгого слабое упорядочивание (например, если для разных точек a и b оба a < b и b < a ложны, но точки не равны), поведение std::map будет неопределённым.