Когда класс можно хранить в контейнере std::set?

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

Ответ

Класс или структура могут быть использованы в качестве ключа std::set, если для них определен строгий слабый порядок (Strict Weak Ordering). Это достигается одним из двух способов:

  1. Определение оператора < для типа ключа.
  2. Предоставление пользовательского функтора-компаратора в качестве второго шаблонного параметра std::set.

Пример 1: Использование оператора <

struct Point {
    int x, y;
    // Определяем порядок: сначала по x, затем по y
    bool operator<(const Point& other) const {
        return std::tie(x, y) < std::tie(other.x, other.y);
    }
};

std::set<Point> points; // Корректно, используется operator<

Пример 2: Использование пользовательского компаратора

struct Person {
    std::string name;
    int age;
};
// Компаратор, сравнивающий только по возрасту
struct CompareByAge {
    bool operator()(const Person& a, const Person& b) const {
        return a.age < b.age;
    }
};

std::set<Person, CompareByAge> people;
// В этом set нельзя будет иметь двух людей с одинаковым возрастом.

Критические требования к компаратору (Compare):

  • Антисимметричность: Если comp(a, b) == true, то comp(b, a) == false.
  • Транзитивность: Если comp(a, b) == true и comp(b, c) == true, то comp(a, c) == true.
  • Транзитивность эквивалентности: Если !comp(a, b) && !comp(b, a) (эквивалентны) и !comp(b, c) && !comp(c, b), то !comp(a, c) && !comp(c, a).

Без выполнения этих условий поведение std::set не определено (UB).