Я активно использую, std::set<int>
и часто мне просто нужно проверить, содержит ли такой набор число или нет.
Я считаю естественным написать:
if (myset.contains(number))
...
Но из-за отсутствия contains
члена мне нужно написать громоздкое:
if (myset.find(number) != myset.end())
..
или не такое очевидное:
if (myset.count(element) > 0)
..
Есть ли причина для такого дизайнерского решения?
count()
подходом заключается в том, что он выполняет больше работы, чемcountains()
нужно.contains()
который возвращаетbool
бы потерять ценную информацию о том, где элемент находится в коллекции .find()
сохраняет и возвращает эту информацию в виде итератора, поэтому это лучший выбор для универсальной библиотеки, такой как STL. (Это неbool contains()
значит, что это не очень приятно иметь или даже не нужно, хотя.)contains(set, element)
помощью общедоступного интерфейса набора легко написать бесплатную функцию. Таким образом, интерфейс набора функционально завершен; добавление удобного метода просто увеличивает интерфейс без включения каких-либо дополнительных функций, чего нельзя сказать о C ++.Ответы:
Думаю, это было, вероятно, потому, что они пытались сделать
std::set
иstd::multiset
максимально похожи. (И, очевидно,count
имеет вполне разумное значение дляstd::multiset
.)Лично я считаю это ошибкой.
Это будет не так уж плохо, если вы сделаете вид, что
count
это просто орфографическая ошибка,contains
и напишете тест как:Хотя все равно обидно.
источник
.end()
).contains()
на том основании, что он был бы избыточным, поскольку для любогоstd::set<T> s
иT t
результатs.contains(t)
точно идентичен результатуstatic_cast<bool>(s.count(t))
. Поскольку использование значения в условном выражении приведет к его неявному преобразованиюbool
, они, возможно, сочли, чтоcount()
это достаточно хорошо служит цели.if (myset.ICanHaz(element)) ...
: DЧтобы иметь возможность писать
if (s.contains())
,contains()
он должен возвращатьbool
(или типbool
, в который можно преобразовать , это другая история), как этоbinary_search
делает.Основная причина позади дизайнерского решения не делать это таким образом, что ,
contains()
который возвращаетbool
бы потерять ценную информацию о том, где элемент находится в коллекции .find()
сохраняет и возвращает эту информацию в виде итератора, поэтому это лучший выбор для универсальной библиотеки, такой как STL. Это всегда было руководящим принципом для Алекса Степанова, как он часто объяснял (например, здесь ).Что касается
count()
подхода в целом, то, хотя это часто хороший обходной путь, проблема заключается в том, что он выполняет больше работы, чемcontains()
должен был бы сделать .Это не значит, что
bool contains()
он не очень полезен или даже необходим. Некоторое время назад мы долго обсуждали эту самую проблему в группе «Стандарт ISO C ++ - будущие предложения».источник
contains()
что, очевидно, это будет сильно взаимодействовать с существующими контейнерами и алгоритмами, на которые будут сильно влиять концепции и диапазоны - в то время, которое ожидается в C ++ 17 - и Я был убежден (в результате обсуждения, а также нескольких личных обменов электронной почтой), что лучше сначала дождаться их. Конечно, в 2015 году не было ясно, что ни концепции, ни диапазоны не войдут в C ++ 17 (на самом деле, на это возлагались большие надежды). Однако я не уверен, что стоит заниматься этим сейчас.std::set
(об этом и спрашивается) я не понимаю, какcount
работает больше, чемcontains
нужно. Реализация glibccount
(примерно)return find(value) == end() ? 0 : 1;
. Помимо подробностей о тернарном операторе и просто возврате!= end()
(который, как я ожидал, оптимизатор удалит), я не вижу, как там еще работать.myset.contains()
(если он существует), это будет довольно убедительным признаком того, что эта информация не представляет ценности ( пользователю в этом контексте).count()
больше работы, чемcontains()
нужноstd::set
? Он уникален, поэтомуcount()
может бытьreturn contains(x) ? 1 : 0;
точно таким же.Его не хватает, потому что его никто не добавил. Никто не добавил его, потому что контейнеры из STL,
std
включенные в библиотеку, были разработаны с минимальным интерфейсом. (Обратите внимание, чтоstd::string
это не произошло из STL таким же образом).Если вас не беспокоит какой-то странный синтаксис, вы можете подделать его:
использование:
По сути, вы можете писать методы расширения для большинства
std
типов C ++, используя эту технику.Имеет смысл просто сделать это:
но меня забавляет метод метода расширения.
На самом деле печально то, что написание эффективного
contains
может быть быстрее наmultimap
илиmultiset
, поскольку им просто нужно найти один элемент, аcount
нужно найти каждый из них и посчитать их .Мультимножество, содержащее 1 миллиард копий 7 (вы знаете, на случай, если у вас закончатся), может иметь очень медленное
.count(7)
, но могло быть очень быстроеcontains(7)
.С помощью вышеупомянутого метода расширения мы могли бы сделать его быстрее в этом случае, используя
lower_bound
, сравниваяend
, а затем сравнивая с элементом. Однако выполнение этого как для неупорядоченного, так и для упорядоченного мяуканья потребует необычных перегрузок SFINAE или конкретных контейнеров.источник
std::set
не может содержать дубликатов и поэтомуstd::set::count
всегда будет возвращать0
или1
.std::multiset::count
canbackticks
слова "набор" потому, что я не говорюstd::set
конкретно о нем. Чтобы вамВы изучаете конкретный случай и не видите картины в целом. Как указано в документации,
std::set
соответствует требованиям концепции AssociativeContainer . Для этой концепции не имеет смысла иметьcontains
метод, поскольку он в значительной степени бесполезен дляstd::multiset
иstd::multimap
, ноcount
отлично работает для всех из них. Хотя методcontains
может быть добавлен в качестве псевдонимаcount
дляstd::set
,std::map
и их хеш - версии (какlength
дляsize()
вstd::string
), но выглядит как библиотеки создатели не видели реальную потребность в нем.источник
string
это чудовище: он существовал до STL, где у него былиlength
и все те методы, которые основаны на индексах, а затем был «контейнерован», чтобы соответствовать модели STL ... без удаления существующих методов по причинам обратной совместимости . См. GotW # 84: Monoliths Unstrung =>string
действительно нарушает принцип проектирования «минимальное количество функций-членов».contains
на множестве / карте трудозатрат равен, но может быть выполнен быстрее, чемcount
на мультинаборе / мульти- карте .contains
метода.size()
иempty()
есть дубликаты, но во многих контейнерах есть и то, и другое.Хотя я не знаю, почему
std::set
нет,contains
ноcount
который только когда-либо возвращает0
или1
, вы можете написать шаблоннуюcontains
вспомогательную функцию следующим образом:И используйте это так:
источник
contains
метод существует, просто он назван глупо.std::string
кашельstd.::string
НЕ является частью STL! Это часть стандартной библиотеки и шаблон задним числом ...count
может быть медленнее, так как ему фактически нужно сделать дваfind
s, чтобы получить начало и конец диапазона, если код используется совместно сmultiset
.contains
. Я не вижу в этом ничего плохого. @MarkRansom, маленький SFINAE предназначен для предотвращения привязки этого шаблона к вещам, которых он не должен.Истинная причина этого для
set
меня загадка, но одно из возможных объяснений того же дизайнаmap
может заключаться в том, чтобы люди случайно не написали неэффективный код:Это приведет к двум
map
поискам.Вместо этого вы вынуждены получить итератор. Это дает вам мысленный намек на то, что вы должны повторно использовать итератор:
который потребляет только один
map
поиск.Когда мы понимаем, что
set
иmap
созданы из одной плоти, мы можем применить этот принцип также кset
. То есть, если мы хотим воздействовать на элементset
только в том случае, если он присутствует вset
, этот дизайн может помешать нам написать такой код:Конечно, все это лишь домыслы.
источник
if (myMap.count("Meaning of universe"))
нормально писать , так что ...?contains
как сcount
.Начиная с c ++ 20,
bool contains( const Key& key ) const
доступен.
источник
А что насчет binary_search?
источник
std::unordered_set
, но сработалоstd::set
бы.contains () должен возвращать bool. Используя компилятор C ++ 20, я получаю следующий вывод кода:
источник
Другая причина в том, что это может создать у программиста ложное впечатление, что std :: set - это набор в смысле теории математических множеств. Если они это реализуют, то возникнет множество других вопросов: если в std :: set есть contains () для значения, почему у него нет его для другого набора? Где объединение (), пересечение () и другие операции над множествами и предикаты?
Ответ, конечно же, заключается в том, что некоторые из заданных операций уже реализованы как функции в (std :: set_union () и т. Д.), А другие так же тривиально реализованы, как contains (). Функции и функциональные объекты лучше работают с математическими абстракциями, чем члены объектов, и они не ограничены конкретным типом контейнера.
Если кому-то нужно реализовать полную функциональность математического набора, у него есть не только выбор базового контейнера, но также выбор деталей реализации, например, будет ли его функция theory_union () работать с неизменяемыми объектами, лучше подходящими для функционального программирования , или он изменит свои операнды и сэкономит память? Будет ли он реализован как объект функции с самого начала или лучше реализовать C-функцию и использовать std :: function <> при необходимости?
На данный момент std :: set - это просто контейнер, хорошо подходящий для реализации set в математическом смысле, но он почти так же далек от теоретического набора, как std :: vector от теоретического вектора.
источник