← Назад к вопросам

Какие объекты можно хранить в std::set?

1.8 Middle🔥 161 комментариев
#Исключения и обработка ошибок#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Какие объекты можно хранить в std::set

std::set — контейнер стандартной библиотеки C++, хранящий уникальные отсортированные элементы. В него можно помещать объекты при соблюдении определённых условий.

Основное требование: оператор сравнения

Главное условие: объект должен поддерживать оператор сравнения (по умолчанию operator<).

std::set использует binary search tree для хранения, поэтому элементы должны быть упорядочиваемы. Это нужно для:

  • Определения позиции вставки нового элемента
  • Проверки уникальности
  • Быстрого поиска O(log n)

1. Встроенные типы данных

Все встроенные типы уже поддерживают оператор <, поэтому хранятся без проблем:

std::set<int> intSet = {5, 2, 8, 1};
std::set<double> doubleSet = {3.14, 2.71, 1.41};
std::set<std::string> stringSet = {"apple", "banana", "cherry"};
std::set<char> charSet = {a, b, c};
std::set<bool> boolSet = {true, false};  // OK, но обычно бесполезно

for (const auto& elem : intSet) {
    std::cout << elem << " ";  // 1 2 5 8
}

Элементы автоматически сортируются в порядке возрастания.

2. Указатели на встроенные типы

std::set<int*> intPtrSet;
int a = 5, b = 10, c = 3;
intPtrSet.insert(&a);
intPtrSet.insert(&b);
intPtrSet.insert(&c);

// Сравниваются сами указатели (адреса), не значения!
for (auto ptr : intPtrSet) {
    std::cout << "Address: " << ptr << " Value: " << *ptr << std::endl;
}

Важно: сравниваются адреса памяти, а не значения. Это редко полезно. Используй std::set<std::shared_ptr<T>> вместо сырых указателей.

3. Пользовательские классы

Для пользовательских классов нужно определить оператор сравнения:

class Person {
public:
    std::string name;
    int age;
    
    Person(const std::string& n, int a) : name(n), age(a) {}
    
    // Оператор < для сравнения
    bool operator<(const Person& other) const {
        if (age != other.age)
            return age < other.age;  // сортируем по возрасту
        return name < other.name;     // если возраст равен, по имени
    }
};

std::set<Person> people;
people.insert(Person("Alice", 30));
people.insert(Person("Bob", 25));
people.insert(Person("Charlie", 30));

for (const auto& p : people) {
    std::cout << p.name << " (" << p.age << ")" << std::endl;
}
// Выведет: Bob (25), Alice (30), Charlie (30)

4. Совместимые компараторы (функции сравнения)

Если оператор < не подходит, можно использовать custom comparator:

struct CompareByName {
    bool operator()(const Person& a, const Person& b) const {
        return a.name < b.name;
    }
};

std::set<Person, CompareByName> peopleByName;
peopleByName.insert(Person("Alice", 30));
peopleByName.insert(Person("Bob", 25));

// Сортируется по имени

Или через лямбда (C++11):

auto cmp = [](const Person& a, const Person& b) {
    return a.name < b.name;
};
std::set<Person, decltype(cmp)> peopleByName(cmp);

5. std::pair и std::tuple

std::set<std::pair<int, std::string>> pairSet;
pairSet.insert({1, "one"});
pairSet.insert({2, "two"});
pairSet.insert({1, "ONE"});  // вставится, т.к. отличается значение

// std::pair сравнивается лексикографически
// сначала по первому элементу, потом по второму

for (const auto& [num, str] : pairSet) {
    std::cout << num << ": " << str << std::endl;
}

6. Контейнеры (векторы, списки)

std::set<std::vector<int>> vectorSet;
vectorSet.insert({1, 2, 3});
vectorSet.insert({1, 2});
vectorSet.insert({2, 1, 3});

// std::vector поддерживает operator<, сравнивает лексикографически
for (const auto& v : vectorSet) {
    for (int x : v) std::cout << x << " ";
    std::cout << std::endl;
}

7. Умные указатели

std::set<std::shared_ptr<Person>> peopleSet;
peopleSet.insert(std::make_shared<Person>("Alice", 30));
peopleSet.insert(std::make_shared<Person>("Bob", 25));

// Сравнивают указатели (адреса), не объекты!
// Обычно это не нужно. Используй custom comparator:

auto cmp = [](const std::shared_ptr<Person>& a, 
              const std::shared_ptr<Person>& b) {
    return a->name < b->name;
};
std::set<std::shared_ptr<Person>, decltype(cmp)> peopleByName(cmp);

Что НЕЛЬЗЯ хранить без доработок

Объекты без оператора сравнения:

struct Data {
    int x, y;
    // нет operator<
};

std::set<Data> dataSet;  // ERROR - нет способа сравнить

Решение: добавь оператор < или используй custom comparator.

Оптимизация

// Используй move semantics для больших объектов
std::set<std::string> largeStrings;
largeStrings.insert(std::string(1000, a));  // копируется в set

// С C++17 можно использовать emplace
largeStrings.emplace("constructed in place");

// Для больших объектов рассмотри set<shared_ptr<T>>
std::set<std::shared_ptr<LargeObject>> objects;
objects.insert(std::make_shared<LargeObject>());

Итого

В std::set можно хранить:

  • Встроенные типы (int, double, char и т.д.)
  • Строки (std::string)
  • Пользовательские классы с operator<
  • Контейнеры (vector, list, string)
  • Пары и кортежи
  • Умные указатели
  • Любые объекты с custom comparator

Главное правило: объект должен поддерживать определение порядка сортировки — либо через встроенный оператор <, либо через custom comparator.

Какие объекты можно хранить в std::set? | PrepBro