В Руководство по стилю Google C++раздел о перегрузке оператора рекомендует не перегружать операторы Любые («кроме редких, особых обстоятельств»). В частности, он рекомендует:
In particular, do not overload
operator==oroperator<just so that your class can be used as a key in an STL container; instead, you should create equality and comparison functor types when declaring the container.
Я немного не уверен в том, как будет выглядеть такой функтор, но мой главный вопрос: Почему, хотите ли вы написать для этого свои собственные функторы? Разве определение operator< и использование стандартной функции std::less<T> не было бы проще? Есть ли преимущества в использовании одного перед другим?





Что ж, согласно веб-странице, которую вы цитируете, у функторов не так много преимуществ («[операторы] могут обмануть нашу интуицию, заставив думать, что дорогие операции - это дешевые встроенные операции»).
Я считаю, что нужно стремиться сделать классы первоклассными объектами как можно больше, что для меня означает, что они понимают как можно больше операторов, насколько это разумно.
Прошло много времени с тех пор, как я написал функтор, но он будет выглядеть примерно так:
class MyClass {....}
class LessThanMyClass : std:binary_function<MyClass, MyClass, bool>
{
public bool operator()(MyClass lhs, MyClass rhs)
{ return /* determine if lhs < rhs */ ; }
}
vector<MyClass> objs;
std::sort(objs.begin(), objs.end(), LessThanMyClass());
}
Функтор - это класс с operator (). В этом случае метод примет два параметра сравниваемого типа и вернет результат bool, если первый меньше второго.
Обновлено: чтобы основываться на том, что сказал Джеймс Карран, вы можете определить свой функтор внутри класса. Так, например:
class MyClass
{
struct LessThan : public std::binary_function<MyClass, MyClass, bool>
{
bool operator()(const MyClass & first, const MyClass & second) const
{
return first.key < second.key;
}
};
};
По иронии судьбы, функтор также требует переопределения оператора (оператора вызова функции - operator ()), поэтому я не уверен, в чем их суть.
За исключением более фундаментальных типов, операция «меньше чем» не всегда тривиальна, и даже равенство может варьироваться от ситуации к ситуации.
Представьте себе ситуацию с авиакомпанией, которая хочет присвоить всем пассажирам посадочные номера. Этот номер отражает порядок посадки (разумеется). Итак, что определяет, кто стоит перед кем? Вы можете просто взять порядок, в котором регистрировались клиенты - в этом случае операция «меньше, чем» будет сравнивать время регистрации. Вы также можете учитывать цену, которую клиенты платили за свои билеты - меньше, чем сейчас, сравнивая цены на билеты.
… и так далее. В общем, просто не имеет смысла определять operator < в классе Passenger, хотя может потребоваться, чтобы пассажиры находились в отсортированном контейнере. Думаю, именно этого и предостерегает Google.
Я согласен. Чтобы расширить: «Редкие, особые обстоятельства», конечно, относительное понятие, если вы изучаете все математические классы, это противоположно редкому и особому. Я думаю, что все программисты должны принять (и дать) их совет с некоторой долей скепсиса. . Нет лучшего способа что-либо сделать (скепсис: в некоторых редких, особых случаях есть) =)
Как правило, определение operator< лучше и проще.
Случай, когда вы захотите использовать функторы, - это когда вам нужно несколько способов сравнения определенного типа. Например:
class Person;
struct CompareByHeight {
bool operator()(const Person &a, const Person &b);
};
struct CompareByWeight {
bool operator()(const Person &a, const Person &b);
};
В этом случае может не быть хорошего способа по умолчанию для сравнения и упорядочивания людей, поэтому лучше не определять operator< и использовать функторы. Вы также можете сказать, что обычно люди упорядочиваются по высоте, и поэтому operator< просто вызывает CompareByHeight, и любой, кому нужно упорядочить Person по весу, должен явно использовать CompareByWeight.
Часто проблема заключается в том, что определение функторов остается на усмотрение пользователя класса, так что вы, как правило, получаете много переопределений одного и того же, всякий раз, когда класс необходимо использовать в упорядоченном контейнере.
Я бы, наверное, не стал доходить до руководства по стилю Google.
Я думаю, что они понимают, что когда вы перегружаете operator< и operator==, вы принимаете решение для каждого использования типа, в то время как типы функторов применяются только к этой коллекции.
Если вам нужны компараторы только для того, чтобы поместить элемент в коллекцию, то лучше иметь функцию специально для этого контекста, а не операторы, которые будут применяться во всех контекстах.
Вы можете отсортировать заказы на покупку в хронологическом порядке, но в целом имеет смысл сравнивать их по общей цене. Если мы перегружаем operator< для сравнения дат, чтобы мы могли загрузить их в коллекцию, мы представляем риск того, что другой клиент может неправильно использовать наш operator<, который, по их мнению, сравнивает общие цены.
Я думаю, что сообщение об отсутствии определения operator <заключается в том, что упорядочение является свойством коллекции, а не объекта. Различные коллекции одних и тех же объектов могут иметь разный порядок. Поэтому при указании типа коллекции следует использовать отдельный функтор, а не оператор <.
Однако на практике многие ваши классы могут иметь естественный порядок, и это единственный порядок, используемый в коллекциях в вашем приложении. В других случаях порядок может даже не иметь отношения к приложению, а только к коллекции, чтобы она могла найти элементы позже. В этих случаях имеет смысл определить operator <.
Помните, что когда мы разрабатываем объектные модели, мы моделируем только подмножество реального мира. В реальном мире может быть множество различных способов ранжирования объектов одного и того же класса, но в области приложения, в которой мы работаем, может быть один из подходящих.
Если в коде возникает необходимость во втором порядке, который столь же важен, как и первый, следует провести рефакторинг класса, чтобы удалить operator <и поместить обе функции ранжирования в отдельные функторы. Это показывает намерение, что ни один рейтинг не важнее других.
Что касается арифметических операторов, вам не следует перегружать их, если вы не реализуете арифметический тип.
Конечно, из каждого правила есть исключения. Если вы не знаете, следует ли делать исключение, вероятно, не следует. Опыт будет вашим проводником.
Я считаю, что Руководство по стилю Google слишком ограничительно. Он также запрещает использование
coutиcin.