Сортировка такого vector
или любого другого применимого (изменяемого входного итератора) диапазона пользовательских объектов типа X
может быть достигнута с использованием различных методов, особенно включая использование стандартных библиотечных алгоритмов, таких как
Поскольку большинство методов для получения относительного упорядочения X
элементов уже опубликованы, я начну с некоторых заметок о том, «почему» и «когда» использовать различные подходы.
«Лучший» подход будет зависеть от разных факторов:
- Является ли сортировка диапазонов
X
объектов общей или редкой задачей (будут ли эти диапазоны сортироваться в нескольких разных местах в программе или пользователями библиотеки)?
- Является ли требуемая сортировка «естественной» (ожидаемой) или существует несколько способов сравнения типа с самим собой?
- Производительность - это проблема, или диапазоны сортировки
X
объектов должны быть надежными?
Если сортировка диапазонов X
является распространенной задачей, и ожидаемая сортировка должна ожидаться (то есть, X
просто оборачивает одно фундаментальное значение), то, вероятно, будет идти на перегрузкуoperator<
так как она позволяет сортировку без какого-либо размытия (например, правильную передачу соответствующих компараторов) и многократно дает ожидаемую полученные результаты.
Если сортировка является распространенной задачей или, вероятно, потребуется в разных контекстах, но есть несколько критериев, которые можно использовать для сортировки X
объектов, я бы выбрал Functors (перегруженные operator()
функции пользовательских классов) или указатели на функции (т.е. один функтор / функция для лексического упорядочения и еще один для естественного упорядочения).
Если сортировка диапазонов типов X
необычна или маловероятна в других контекстах, я склонен использовать лямбды вместо того, чтобы загромождать любое пространство имен большим количеством функций или типов.
Это особенно верно, если сортировка не является «чистой» или «естественной» в некотором роде. Вы можете легко понять логику упорядочения, если посмотреть на лямбду, которая применяется на месте, тогда как operator<
на первый взгляд она непрозрачна, и вам придется поискать определение, чтобы знать, какая логика упорядочения будет применяться.
Однако обратите внимание, что одно operator<
определение - это одна точка отказа, тогда как несколько лямб - это несколько точек отказа и требуют большей осторожности.
Если определение operator<
где недоступно, где сортировка выполнена / шаблон сортировки скомпилирован, компилятор может быть вынужден сделать вызов функции при сравнении объектов, вместо того, чтобы включать логику упорядочения, которая может быть серьезным недостатком (по крайней мере, когда оптимизация времени соединения / генерация кода не применяется).
Способы достижения сопоставимости class X
для использования стандартных алгоритмов сортировки библиотеки
Пусть std::vector<X> vec_X;
иstd::vector<Y> vec_Y;
1. Перегрузите T::operator<(T)
или operator<(T, T)
и используйте стандартные библиотечные шаблоны, которые не ожидают функции сравнения.
Либо элемент перегрузки operator<
:
struct X {
int i{};
bool operator<(X const &r) const { return i < r.i; }
};
// ...
std::sort(vec_X.begin(), vec_X.end());
или бесплатно operator<
:
struct Y {
int j{};
};
bool operator<(Y const &l, Y const &r) { return l.j < r.j; }
// ...
std::sort(vec_Y.begin(), vec_Y.end());
2. Используйте указатель функции с пользовательской функцией сравнения в качестве параметра функции сортировки.
struct X {
int i{};
};
bool X_less(X const &l, X const &r) { return l.i < r.i; }
// ...
std::sort(vec_X.begin(), vec_X.end(), &X_less);
3. Создайте bool operator()(T, T)
перегрузку для пользовательского типа, которая может быть передана как функтор сравнения.
struct X {
int i{};
int j{};
};
struct less_X_i
{
bool operator()(X const &l, X const &r) const { return l.i < r.i; }
};
struct less_X_j
{
bool operator()(X const &l, X const &r) const { return l.j < r.j; }
};
// sort by i
std::sort(vec_X.begin(), vec_X.end(), less_X_i{});
// or sort by j
std::sort(vec_X.begin(), vec_X.end(), less_X_j{});
Эти определения функциональных объектов могут быть написаны немного более обобщенно, используя C ++ 11 и шаблоны:
struct less_i
{
template<class T, class U>
bool operator()(T&& l, U&& r) const { return std::forward<T>(l).i < std::forward<U>(r).i; }
};
который может быть использован для сортировки любого типа с i
поддержкой членов <
.
4. Передайте анонимное закрытие (лямбда) в качестве параметра сравнения в функции сортировки.
struct X {
int i{}, j{};
};
std::sort(vec_X.begin(), vec_X.end(), [](X const &l, X const &r) { return l.i < r.i; });
Где C ++ 14 обеспечивает еще более общее лямбда-выражение:
std::sort(a.begin(), a.end(), [](auto && l, auto && r) { return l.i < r.i; });
который можно обернуть в макрос
#define COMPARATOR(code) [](auto && l, auto && r) -> bool { return code ; }
сделать обычное создание компаратора довольно плавным:
// sort by i
std::sort(v.begin(), v.end(), COMPARATOR(l.i < r.i));
// sort by j
std::sort(v.begin(), v.end(), COMPARATOR(l.j < r.j));