Различия между различными пользовательскими функциями компаратора в C++
Я обнаружил, что существуют различные способы определения пользовательских функций сравнения для пользовательского объекта. Я хотел бы знать, что я должен принять во внимание, прежде чем выбрать одно из них.
Если у меня есть объект student, я могу написать пользовательскую функцию сравнения следующими способами.
struct Student
{
string name;
uint32_t age;
// Method 1: Using operator <
bool operator<(const Student& ob)
{
return age < ob.age;
}
};
// Method 2: Custom Compare Function
bool compStudent(const Student& a, const Student& b)
{
return a.age < b.age;
}
// Method 3: Using operator ()
struct MyStudComp
{
bool operator() (const Student& a, const Student& b)
{
return a.age < b.age;
}
}obComp;
Для сортировки вектора студентов я могу использовать любой из следующих методов.
vector<Student> studs; // Consider I have this object populated
std::sort(studs.begin(), studs.end()); // Method 1
std::sort(studs.begin(), studs.end(), compStudent); // Method 2
std::sort(studs.begin(), studs.end(), obComp); // Method 3
// Method 4: Using Lambda
sort(studs.begin(), studs.end(),
[](const Student& a, const Student& b) -> bool
{
return a.age < b.age;
});
Чем отличаются эти методы и как я должен выбирать между ними. Спасибо продвижение.
4 ответа:
Производительность между различными методами не очень отличается, однако использование
<
позволит вам быть более гибким и значительно упростит использование встроенных модулей. Я также думаю, что использование()
Немного странно.Более серьезная проблема в вашем примере заключается в том, что ваши методы должны использовать const refs вместо значений. То есть
bool operator<(Student ob)
может бытьfriend bool operator<(const Student& ls, const Student& rs){...}
. Кроме того, смотрите здесь для некоторых примеров различных вещей, которые следует учитывать при перегрузке операторов.
Производительность не будет заметно отличаться. Но во многих случаях удобно (и ожидаемо) иметь
operator<
, поэтому я бы пошел на это через специальную функцию сравнения.
На самом деле нет "правильного" способа как такового, но если для вашего объекта имеет смысл иметь пользовательские компараторы (т. е.
operator<
и т. д.) тогда было бы разумно просто использовать их. Однако вы можете захотеть отсортировать ваш объект на основе другого члена поля, и поэтому предоставление пользовательского лямбда-кода на основе этих сравнений полей будет иметь смысл в этом случае.Например, ваш
Student
класс В настоящее время использует перегруженныйoperator<
Для сравнения возрастов учащихся, поэтому, если вы сортируете контейнерStudent
s на основе возраст тогда просто используйте этот оператор неявно. Тем не менее, вы можете захотеть (в другое время) выполнить сортировку на основе имен, чтобы в этом случае вы могли предоставить пользовательский лямбда-код в качестве наиболее элегантного метода:Где имена учащихся сортируются с помощью лексикографических сравнений.std::vector<Student> vec; // populate vec std::sort(vec.begin(), vec.end(), [](auto& lhs, auto& rhs) { return lhs.name < rhs.name; });
Они различаются своими имплицитными заявлениями о намерениях. Вы должны использовать форму, которая наиболее лаконично выражает ваше намерение.Чем отличаются эти методы и как я должен выбирать между ними.
Полагаться на
operator<
означает для кого-то, читающего ваш код, что ваши объектынеявно упорядочены , как числа или строки. Они должны быть вещами, которые люди сказали бы: "ну, очевидно, x приходит раньше y".Если упорядоченность карты больше абстрактно, тогда функция упорядочения может быть лучше, потому что она выражает идею о том, что вы устанавливаете порядок на карте, который может не быть естественным порядком.
В приведенном примере я мог бы выразить намерение в любом функциональном объекте, называемом
ageIsLess
, например. Как читатель кода, использующий карту, теперь полностью осознает намерение.Например:
#include <cstdint> #include <set> #include <string> #include <algorithm> #include <iterator> struct Student { std::string name; std::uint32_t age; }; struct ByAscendingAge { bool operator() (const Student& a, const Student& b) const { return a.age < b.age; } }; bool age_is_less(const Student& l, const Student& r) { return l.age < r.age; }; bool name_is_less(const Student& l, const Student& r) { return l.name < r.name; }; int main() { // this form expresses the intent that any 2 different maps of this type can have different ordering using students_by_free_function = std::set<Student, bool (*)(const Student&, const Student&)>; // ordered by age students_by_free_function by_age_1(age_is_less); // ordered by name students_by_free_function by_name_1(name_is_less); // above two maps are the same type so we can assign them, which implicitly reorders by_age_1 = by_name_1; // this form expresses the intent that the ordering is a PROPERTY OF THIS TYPE OF SET using students_by_age = std::set<Student, ByAscendingAge>; // note that we don't need a comparator in the constructor students_by_age by_age_2; // by_age_2 = by_age_1; // not allowed because the sets are a different type // but we can assign iterator ranges of course std::copy(std::begin(by_age_1), std::end(by_age_1), std::inserter(by_age_2, std::end(by_age_2))); }