Пользовательские компараторы
Управляйте порядком сортировки
«Пользовательские компараторы» — бесплатный урок C++ Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C++ Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C++ Academy содержит 4 уроков всего.
Зачем нужны пользовательские компараторы
По умолчанию упорядоченные контейнеры сортируют элементы с помощью std::less (по возрастанию). Пользовательский компаратор позволяет изменить этот порядок, например сортировать по убыванию или по определённому полю.
#include <iostream>
#include <set>
int main() {
std::set<int> ascending{3, 1, 2};
for (int x : ascending) std::cout << x << ' ';
std::cout << '\n';
return 0;
}Сортировка по убыванию с помощью std::greater
Самый простой пользовательский компаратор — стандартный функтор std::greater, который сортирует элементы по убыванию.
#include <iostream>
#include <set>
#include <functional>
int main() {
std::set<int, std::greater<int>> s{3, 1, 2};
for (int x : s) std::cout << x << ' ';
std::cout << '\n';
return 0;
}Компаратор для отображения
Компаратор — это третий параметр шаблона std::map. В данном случае ключи сортируются от большего к меньшему.
#include <iostream>
#include <map>
#include <functional>
int main() {
std::map<int, std::string, std::greater<int>> m{
{1, "one"}, {3, "three"}, {2, "two"}
};
for (const auto& [k, v] : m) std::cout << k << ':' << v << ' ';
std::cout << '\n';
return 0;
}Как работают компараторы
Компаратор — это вызываемый объект, принимающий два аргумента и возвращающий true, если первый элемент должен находиться перед вторым. Он должен задавать строгое слабое упорядочивание.
#include <iostream>
struct Less {
bool operator()(int a, int b) const { return a < b; }
};
int main() {
Less cmp;
std::cout << std::boolalpha << cmp(2, 5) << '\n';
std::cout << cmp(5, 2) << '\n';
return 0;
}Компаратор на основе пользовательской структуры
Определите собственный функтор-структуру с помощью operator() и передайте её тип как компаратор.
#include <iostream>
#include <set>
struct ByAbs {
bool operator()(int a, int b) const {
return (a < 0 ? -a : a) < (b < 0 ? -b : b);
}
};
int main() {
std::set<int, ByAbs> s{-5, 3, -1, 4};
for (int x : s) std::cout << x << ' ';
std::cout << '\n';
return 0;
}Сортировка строк по длине
Компараторы могут сравнивать любые свойства. Здесь строки упорядочиваются по длине, а при одинаковой длине — в алфавитном порядке.
#include <iostream>
#include <set>
#include <string>
struct ByLen {
bool operator()(const std::string& a, const std::string& b) const {
if (a.size() != b.size()) return a.size() < b.size();
return a < b;
}
};
int main() {
std::set<std::string, ByLen> s{"bbb", "a", "cc", "dd"};
for (const auto& x : s) std::cout << x << ' ';
std::cout << '\n';
return 0;
}Зачем нужны дополнительные критерии сравнения
Если компаратор считает, что ни один из элементов не должен находиться перед другим, контейнер считает их равными. В set это означает, что один элемент будет отброшен как дубликат.
#include <iostream>
#include <set>
#include <string>
struct LenOnly {
bool operator()(const std::string& a, const std::string& b) const {
return a.size() < b.size();
}
};
int main() {
std::set<std::string, LenOnly> s{"ab", "cd", "x"};
std::cout << s.size() << " elements\n";
return 0;
}Лямбда-выражения как компараторы
Можно использовать лямбда-выражение, передав его тип через decltype, а само лямбда-выражение — конструктору.
#include <iostream>
#include <set>
int main() {
auto cmp = [](int a, int b) { return a > b; };
std::set<int, decltype(cmp)> s(cmp);
s.insert(1);
s.insert(3);
s.insert(2);
for (int x : s) std::cout << x << ' ';
std::cout << '\n';
return 0;
}Компараторы в priority_queue
Компараторы также настраивают std::priority_queue. С std::greater он превращается в кучу с минимальным элементом наверху.
#include <iostream>
#include <queue>
#include <vector>
#include <functional>
int main() {
std::priority_queue<int, std::vector<int>, std::greater<int>> pq;
pq.push(5); pq.push(1); pq.push(3);
while (!pq.empty()) { std::cout << pq.top() << ' '; pq.pop(); }
std::cout << '\n';
return 0;
}Сравнение пар
Чтобы сортировать набор пар по второму элементу, напишите компаратор, который проверяет .second.
#include <iostream>
#include <set>
#include <utility>
struct BySecond {
bool operator()(const std::pair<int,int>& a, const std::pair<int,int>& b) const {
return a.second < b.second;
}
};
int main() {
std::set<std::pair<int,int>, BySecond> s{{1, 9}, {2, 3}, {3, 6}};
for (const auto& p : s) std::cout << p.first << ':' << p.second << ' ';
std::cout << '\n';
return 0;
}Прозрачные компараторы
Использование std::less<> (с пустыми угловыми скобками) включает гетерогенный поиск в C++14 и позволяет избежать временных преобразований ключей.
#include <iostream>
#include <set>
#include <functional>
int main() {
std::set<int, std::less<>> s{1, 2, 3};
std::cout << (s.find(2) != s.end() ? "found" : "no") << '\n';
return 0;
}Быстрая проверка
Проверьте, насколько хорошо Вы поняли, как компараторы определяют равенство.
Повторение
Вы узнали, что пользовательские компараторы:
- изменяют порядок через параметр шаблона компаратора контейнера
- могут быть представлены как
std::greater, функтор-структура или лямбда-выражение (черезdecltype) - определяют равенство через эквивалентность, поэтому всегда включайте дополнительный критерий сравнения, чтобы не потерять различные элементы
Следующий курс посвящён быстрому поиску на основе хеширования с помощью std::unordered_map.
Часто задаваемые вопросы
Урок «Пользовательские компараторы» бесплатный?
Да — полный текст урока «Пользовательские компараторы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C++ Academy, подпишись на CoddyKit PRO. Курс C++ Academy содержит 4 уроков всего.
Чему я научусь в уроке «Пользовательские компараторы»?
Управляйте порядком сортировки Ты практикуешь C++ Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C++ Academy?
Предыдущий опыт не требуется. C++ Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Пользовательские компараторы»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C++ Academy?
Да. Каждый урок C++ Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- std::map
- std::set
- multimap и multiset
- Пользовательские компараторы