Я слежу за книгой по изучению C ++ (из фона Python). Я написал это, и это работает:
class CatalogueItem
{
public:
CatalogueItem();
CatalogueItem(int item_code, const string &name, const string &description);
~CatalogueItem() {};
bool operator< (const CatalogueItem &other) const;
...
private:
...
};
...
list<CatalogueItem> my_list;
// this is just me playing around
CatalogueItem items[2];
items[0] = CatalogueItem(4, string("box"), string("it's a box"));
items[1] = CatalogueItem(3, string("cat"), string("it's a cat"));
my_list.push_back(items[0]);
my_list.push_back(items[1]);
my_list.sort();
Часть, которую я пробую, использует оператор <, чтобы позволить списку сортировать себя.
Все это кажется хорошим, но http: //google-styleguide.googlecode. com / svn / trunk / cppguide.xml # Operator_Overloading, кажется, предлагает избегать этого, а именно это и сказано в книге! («В частности, не перегружайте operator == или operator <только для того, чтобы ваш класс можно было использовать в качестве ключа в контейнере STL; вместо этого вы должны создать типы функторов равенства и сравнения при объявлении контейнера.»)
Я понимаю, что «создание типов функторов равенства и сравнения» означает создание функций сравнения, подобных приведенной ниже:
bool my_comparison_function(const CatalogueItem &a, const CatalogueItem &b)
{
// my comparison code here
}
Это то, о чем говорится в руководстве по стилю?
Есть ли у кого-нибудь вариант, какой способ более "правильный"?
J
3 ответа
Тип функтора будет более похож на это:
struct CatalogueItemLessThan
{
bool operator()(const CatalogueItem &a, const CatalogueItem &b)
{
}
};
Тогда использование будет выглядеть так:
list<CatalogueItem> my_list;
// this is just me playing around
CatalogueItem items[2];
items[0] = CatalogueItem(4, string("box"), string("it's a box"));
items[1] = CatalogueItem(3, string("cat"), string("it's a cat"));
my_list.push_back(items[0]);
my_list.push_back(items[1]);
my_list.sort(CatalogueItemLessThan());
Основным преимуществом этого является то, что он позволяет отделить сортировку от самого объекта. Теперь вы можете предоставить столько типов сортировки, сколько захотите, и использовать их в разных местах. (Например, строка может быть отсортирована в лексическом порядке, без учета регистра или "естественным образом".
Преимущество использования функтора по сравнению с произвольной функцией состоит в том, что вы можете передавать параметры в сравнение, чтобы изменить поведение функтора.
В общем, руководство по стилю Google на самом деле не лучшее руководство по стилю (IMHO, особенно их исключение из исключений, но это другое обсуждение). Если объект имеет очевидный порядок сортировки, я часто добавляю по умолчанию operator<
. Если позже я хочу добавить дополнительные порядки сортировки, я добавляю отдельные функции. Если позже мне нужно добавить параметры в порядок сортировки, я превращаю их в функторы. Нет смысла увеличивать сложность до того, как это понадобится.
std::map
, чего нельзя сделать с простой функцией.
Google пытается вам сказать следующее.
Как вы знаете, вы можете перегрузить один и только один оператор '<' для данного типа. Допустим, это работает для вас. Но представьте, что в будущем вам может потребоваться сортировать объекты одного типа в соответствии с каким-либо другим критерием сравнения. Как ты собираешься это сделать? Единственная доступная версия '<' уже занята.
Конечно, вы можете сделать это, написав новую именованную функцию / функтор сравнения (а не оператор «<») и явно предоставив ее алгоритму сортировки. Вы можете написать еще 2, 5, 10 из них. Вы можете писать сколько угодно. Это будет работать. Однако в этот момент в вашем коде будет очевидная асимметрия. Одна функция сравнения реализована как «оператор <». Остальные - как разные поименованные функции / функторы. Есть ли у этой асимметрии веская причина?
Что ж, может быть. Если у вас есть очень четко определенный и очевидный естественный метод сортировки, который применяется к вашему типу, имеет смысл реализовать его как оператор '<'. Это будет основной метод сравнения. А другие, вспомогательные, менее «естественные» методы сравнения могут и должны быть реализованы как именованные функции. Это прекрасно.
Однако что делать, если у вас нет столь очевидного кандидата на «естественное» сравнение? В этом случае отдавать предпочтение одному методу перед другим и «тратить» оператор '<' на произвольно выбранный - не лучшая идея. В этом случае рекомендуется оставить символ «<» в покое и вместо этого использовать именованные функции / функторы.
Другими словами, перегружая '<', вы создаете «избранное» сравнение для данного типа. Если это то, чего вы действительно хотите - продолжайте и делайте это. Но имейте в виду, что во многих случаях создание искусственного и произвольного «фаворита» - не лучшая идея. Не торопитесь с выбором фаворита. Не принимайте "<" слишком рано.
Тип функтора - это тип C ++ (класс или структура), который перегружает оператор ()
, так что экземпляры типа ведут себя как функция. Это похоже на класс, реализующий __call__()
в Python.
Некоторым типам коллекций STL, таким как std::map
, требуется функтор key_compare
для упорядочивания ключей в структурах целого дерева и, таким образом, обеспечения быстрого доступа. По умолчанию это std::less
, который использует operator<
для сравнения значений. Поэтому этот оператор часто предоставляется, чтобы пользовательские классы могли действовать как ключи в std::map
(и т.п.).
Очевидно, что Google не одобряет этого в пользу предоставления собственного функтора сравнения. Итак, вместо реализации operator<
вы можете сделать следующее:
struct my_compare
{
bool operator ()(const CatalogueItem& lhs, const CatalogueItem& rhs)
{
...
}
};
Если для реализации этого вам необходим доступ к закрытым членам, объявите functor
как friend
своего класса.
Похожие вопросы
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который должен быть) скомпилирован с помощью компилятора C ++. Используйте тег для конкретной версии для вопросов, связанных с конкретной версией стандарта [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] или [C ++ 23] и т. Д. .