Сортування такого 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));