Сравнение типов функторов и оператора <

В Руководство по стилю Google C++раздел о перегрузке оператора рекомендует не перегружать операторы Любые («кроме редких, особых обстоятельств»). В частности, он рекомендует:

In particular, do not overload operator== or operator< 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> не было бы проще? Есть ли преимущества в использовании одного перед другим?

Я считаю, что Руководство по стилю Google слишком ограничительно. Он также запрещает использование cout и cin.

rlbond 19.09.2009 22:02
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
10
1
8 818
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Что ж, согласно веб-странице, которую вы цитируете, у функторов не так много преимуществ («[операторы] могут обмануть нашу интуицию, заставив думать, что дорогие операции - это дешевые встроенные операции»).

Я считаю, что нужно стремиться сделать классы первоклассными объектами как можно больше, что для меня означает, что они понимают как можно больше операторов, насколько это разумно.

Прошло много времени с тех пор, как я написал функтор, но он будет выглядеть примерно так:

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.

Я согласен. Чтобы расширить: «Редкие, особые обстоятельства», конечно, относительное понятие, если вы изучаете все математические классы, это противоположно редкому и особому. Я думаю, что все программисты должны принять (и дать) их совет с некоторой долей скепсиса. . Нет лучшего способа что-либо сделать (скепсис: в некоторых редких, особых случаях есть) =)

SinisterRainbow 20.08.2012 16:30

Как правило, определение 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 <и поместить обе функции ранжирования в отдельные функторы. Это показывает намерение, что ни один рейтинг не важнее других.

Что касается арифметических операторов, вам не следует перегружать их, если вы не реализуете арифметический тип.

Конечно, из каждого правила есть исключения. Если вы не знаете, следует ли делать исключение, вероятно, не следует. Опыт будет вашим проводником.

Другие вопросы по теме