Единственный способ сделать это проще - это логический предикат: template <typename T> bool member (T const & item). И это будет реализовано (под прикрытием) с точки зрения линии, о которой вы спрашиваете.
Дон Уэйкфилд
Ответы:
400
Типичный способ проверить существование во многих STL контейнеров , таких как std::map, std::set... это:
это специфично для наборов и карт. векторы, списки и т. д. не имеют функции поиска.
Вильгельмтелл
8
IMO, использующий count (), лучше, потому что он просто короче и конвертируется в bool, как отмечено в ответе Питера. Я не понимаю, почему этот ответ был принят и так много очков ...
Огњен Шобајић
4
Для полноты: векторы / списки могут использовать std :: find std::find(container.begin(), container.end(), element) != container.end():; O (N) проблема остается, конечно ...
Аконкагуа
10
@MichaelMathews В вашем варианте: сначала if(container.find(foo) == container.end())нужно выполнить поиск по дереву, чтобы найти элемент - если он не найден, то нужно выполнить поиск по второму дереву, чтобы найти правильное место вставки. У оригинального варианта if(container.insert(foo).second) {...}есть очарование, что ему нужен только один поиск дерева ...
Аконкагуа
23
есть , set.contains(x)что возвращает BOOL в ++ 20 стандарта C. Я не знаю, почему это заняло у нас до 2020 года.
Грэмвелл
215
Другой способ просто сказать, существует ли элемент, это проверить count()
if(myset.count(x)){// x is in the set, count is 1}else{// count zero, i.e. x not in the set}
Однако в большинстве случаев мне нужен доступ к элементу, где бы я ни проверял его существование.
Так что я все равно должен найти итератор. Тогда, конечно, лучше просто сравнить с этим end.
set< X >::iterator it = myset.find(x);if(it != myset.end()){// do something with *it}
Обратите внимание, что использование count()вместо find()никогда не лучше, но потенциально хуже. Это потому, find()что вернется после первого совпадения, count()всегда будет перебирать все элементы.
Фрерих Раабе
34
@Frerich, это только актуально, multisetи multimapя подумал? Тем не менее, приятно отметить, что :)
Питер
83
std :: set обычно реализуется с упорядоченной древовидной структурой, поэтому count () и find () будут иметь O (logn). Ни один из них не будет перебирать все элементы в наборе.
Алан
14
@FrerichRaabe - Вы уверены? Поскольку возможно setсодержать только один совпадающий член, разве функция не будет реализована таким образом, чтобы останавливаться после нахождения первого элемента, в данном случае, как указывает Питер? Полезный ответ в любом случае!
Дан Ниссенбаум
14
@DanNissenbaum Да, вы совершенно правы (как и Питер + и Алан): для std :: set две функции эквивалентны с точки зрения производительности. Поэтому, хотя первая часть моего комментария ( count()никогда не быстрая, чем find()) по-прежнему имеет место, вторая часть действительно не применима к std::set. Тем не менее, я предполагаю, что можно привести еще один аргумент в пользу find(): он более выразителен, то есть подчеркивает, что вы пытаетесь найти элемент вместо подсчета количества вхождений.
Фрерих Раабе
42
Просто чтобы прояснить, причина, по которой contains()в этих типах контейнеров нет элементов, подобных тем, что это откроет вам возможность писать неэффективный код. Такой метод, вероятно, будет просто this->find(key) != this->end()внутренним, но подумайте, что вы делаете, когда ключ действительно присутствует; в большинстве случаев вы захотите получить элемент и что-то с ним сделать. Это означает, что вам нужно сделать секунду find(), что неэффективно. Лучше использовать поиск напрямую, чтобы вы могли кэшировать свой результат, например так:
auto it = myContainer.find(key);if(it != myContainer.end()){// Do something with it, no more lookup needed.}else{// Key was not present.}
Конечно, если вы не заботитесь об эффективности, вы всегда можете свернуть свои собственные, но в этом случае вам, вероятно, не следует использовать C ++ ...;)
Как насчет наборов? Обычно у вас уже есть элемент, но вы просто хотите проверить, присутствует ли он.
Элазар Лейбович
8
Есть ли у вас какие-либо ссылки на то, является ли это действительной причиной, по которой такой метод / функция не включен в stl, или это просто ваше обоснованное предположение?
Фабио А.
3
@FabioA. Это мое обоснованное предположение.
Тим
1
@ Adhemar, последовательность не совсем сильная сторона STL ... ( list::remove, remove(makes_sense_only_for_vector, iterators)...)
Элазар Лейбович
3
Мне не имеет смысла не включать функцию, потому что кто-то может использовать ее неправильно, если он не знает, что делает. Программирование предназначено для людей, которые могут думать сами за себя и нести ответственность за свой код и его производительность
slawekwin
13
В C ++ 20 мы наконец получим std::set::containsметод.
#include<iostream>#include<string>#include<set>int main(){
std::set<std::string> example ={"Do","not","panic","!!!"};if(example.contains("panic")){
std::cout <<"Found\n";}else{
std::cout <<"Not found\n";}}
Если вы собираетесь добавить containsфункцию, она может выглядеть так:
#include<algorithm>#include<iterator>template<classTInputIterator,class T>inlinebool contains(TInputIterator first,TInputIteratorlast,const T&value){return std::find(first,last,value)!=last;}template<classTContainer,class T>inlinebool contains(constTContainer& container,const T&value){// This works with more containers but requires std::begin and std::end// from C++0x, which you can get either:// 1. By using a C++0x compiler or// 2. Including the utility functions below.return contains(std::begin(container), std::end(container),value);// This works pre-C++0x (and without the utility functions below, but doesn't// work for fixed-length arrays.//return contains(container.begin(), container.end(), value);}template<class T>inlinebool contains(const std::set<T>& container,const T&value){return container.find(value)!= container.end();}
Это работает с std::setдругими контейнерами STL и даже массивами фиксированной длины:
Как указано в комментариях, я непреднамеренно использовал функцию, новую для C ++ 0x ( std::beginи std::end). Вот почти тривиальная реализация VS2010:
namespace std {template<class_Container>inlinetypename_Container::iterator begin(_Container&_Cont){// get beginning of sequencereturn(_Cont.begin());}template<class_Container>inlinetypename_Container::const_iterator begin(const_Container&_Cont){// get beginning of sequencereturn(_Cont.begin());}template<class_Container>inlinetypename_Container::iterator end(_Container&_Cont){// get end of sequencereturn(_Cont.end());}template<class_Container>inlinetypename_Container::const_iterator end(const_Container&_Cont){// get end of sequencereturn(_Cont.end());}template<class_Ty,size_t_Size>inline_Ty*begin(_Ty(&_Array)[_Size]){// get beginning of arrayreturn(&_Array[0]);}template<class_Ty,size_t_Size>inline_Ty*end(_Ty(&_Array)[_Size]){// get end of arrayreturn(&_Array[0]+_Size);}}
@ Adhemar, на самом деле это было неэффективно, но совсем не по той причине, которую вы упомянули.
Сэм Харвелл
@ Пол: Убедитесь, что вы включили специализацию для std::set, и помните, что это уместно, только если единственное, что вам нужно знать, это существование.
Сэм Харвелл
@ 280Z28: std :: begin (контейнер)? Что это за стандарт STL? Это не компилируется на моем GCC.
stefaanv
@stefannv: хех, это новое для C ++ 0x. Я добавил реализацию из моего компилятора выше.
Сэм Харвелл
2
@ Adhemar: Если вы знаете, что набор содержит значение, то вы уже это значение. Единственная причина, по которой вам нужен итератор - это удаление элемента из набора. Если все, что вам нужно, это знать, содержит ли коллекция значение, то это решение не менее эффективно, чем любое другое решение.
Сэм Харвелл
4
Вы также можете проверить, находится ли элемент в наборе или нет при вставке элемента. Версия с одним элементом возвращает пару, в которой для ее элемента pair :: first установлено значение итератора, указывающее либо на вновь вставленный элемент, либо на эквивалентный элемент, уже находящийся в наборе. Элемент pair :: second в паре устанавливается равным true, если новый элемент был вставлен, или false, если эквивалентный элемент уже существует.
Например: предположим, что в наборе уже есть 20 в качестве элемента.
std::set<int> myset;
std::set<int>::iterator it;
std::pair<std::set<int>::iterator,bool> ret;
ret=myset.insert(20);if(ret.second==false){//do nothing}else{//do something}
it=ret.first //points to element 20 already in set.
Если элемент недавно вставлен, то pair :: first будет указывать на позицию нового элемента в наборе.
только что сделал: template <class T> static inline bool содержит (const std :: set <T> & S, T x) {return (S.find (x)! = S.end ()); }
fulmicoton
4
@paul не создает статические глобальные функции. вместо этого поместите вашу функцию в анонимное пространство имен: это C ++ способ создания функций, которые не будут ссылаться на другие модули компиляции. Кроме того, ваш параметр T должен быть константным эталоном, для правильности констант и для эффективности.
Вильгельмтелл
-1: Не шаблонный и не совсем в стиле STL. Это нормально, если вы не используете STL, но если вы используете STL, вы должны, по крайней мере, попытаться следовать его стандартам.
Сэм Харвелл
1
@ 280Z28: Мне жаль, что мой код не соответствует вашим стандартам, я просто показал, что если вам не нравится интерфейс STL, вы можете написать свой собственный. Боже, не шаблон? Насколько шаблонным это должно быть? Ваш пример выглядит хорошо, это не значит, что у меня плохо. Это просто больше сосредоточено на наборе, как было задано ОП.
stefaanv
1
@ 280Z28: я просто делал выводы. Я думал, что люди будут достаточно умны, чтобы получить картину.
stefaanv
2
я использую
if(!my_set.count(that_element))//Element is present...;
Но это не так эффективно, как
if(my_set.find(that_element)!=my_set.end())....;
Моя версия только экономит мое время при написании кода. Я предпочитаю это так для конкурентного кодирования.
Да, count(). Любой, кто не в состоянии понять, что функция, возвращающая целое число, используемая в булевом выражении, проверяет ненулевое, будет иметь много, много других проблем в большом море идиом C / C ++. И, как отмечалось выше, действительно должно быть максимально эффективно для наборов, о чем и шла речь.
Рон Берк
0
Я был в состоянии написать общую containsфункцию для std::listи std::vector,
Извините, я не могу написать блочный код в комментариях, но как насчет template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
fulmicoton
Это не работает, потому что std :: vector нуждается в дополнительном распределителе в качестве аргумента шаблона, а std :: set требует распределителя и меньшего аргумента шаблона. Эти строки woulkd работают: template <template <class, class> class STLContainer, class T, class A> bool содержит (STLContainer <T, A> контейнер, T elt) {return find (container.begin (), container.end ( ), elt)! = container.end (); } template <template <класс, класс, класс> класс STLContainer, класс T, класс L, класс A> bool содержит (контейнер STLContainer <T, A, L>, T elt) {return find (container.begin (), контейнер .end (), elt)! = container.end (); }
tgmath
0
// общий синтаксис
set<int>::iterator ii = find(set1.begin(),set1.end(),"element to be searched");
/ * в приведенном ниже коде я пытаюсь найти элемент 4 и установить int, если он присутствует или нет * /
set<int>::iterator ii = find(set1.begin(),set1.end(),4);if(ii!=set1.end()){
cout<<"element found";
set1.erase(ii);// in case you want to erase that element from set.}
Ответы:
Типичный способ проверить существование во многих STL контейнеров , таких как
std::map
,std::set
... это:источник
std::find(container.begin(), container.end(), element) != container.end()
:; O (N) проблема остается, конечно ...if(container.find(foo) == container.end())
нужно выполнить поиск по дереву, чтобы найти элемент - если он не найден, то нужно выполнить поиск по второму дереву, чтобы найти правильное место вставки. У оригинального вариантаif(container.insert(foo).second) {...}
есть очарование, что ему нужен только один поиск дерева ...set.contains(x)
что возвращает BOOL в ++ 20 стандарта C. Я не знаю, почему это заняло у нас до 2020 года.Другой способ просто сказать, существует ли элемент, это проверить
count()
Однако в большинстве случаев мне нужен доступ к элементу, где бы я ни проверял его существование.
Так что я все равно должен найти итератор. Тогда, конечно, лучше просто сравнить с этим
end
.С ++ 20
В C ++ 20 set получает
contains
функцию, поэтому становится возможным следующее, как упомянуто по адресу: https://stackoverflow.com/a/54197839/895245источник
count()
вместоfind()
никогда не лучше, но потенциально хуже. Это потому,find()
что вернется после первого совпадения,count()
всегда будет перебирать все элементы.multiset
иmultimap
я подумал? Тем не менее, приятно отметить, что :)set
содержать только один совпадающий член, разве функция не будет реализована таким образом, чтобы останавливаться после нахождения первого элемента, в данном случае, как указывает Питер? Полезный ответ в любом случае!count()
никогда не быстрая, чемfind()
) по-прежнему имеет место, вторая часть действительно не применима кstd::set
. Тем не менее, я предполагаю, что можно привести еще один аргумент в пользуfind()
: он более выразителен, то есть подчеркивает, что вы пытаетесь найти элемент вместо подсчета количества вхождений.Просто чтобы прояснить, причина, по которой
contains()
в этих типах контейнеров нет элементов, подобных тем, что это откроет вам возможность писать неэффективный код. Такой метод, вероятно, будет простоthis->find(key) != this->end()
внутренним, но подумайте, что вы делаете, когда ключ действительно присутствует; в большинстве случаев вы захотите получить элемент и что-то с ним сделать. Это означает, что вам нужно сделать секундуfind()
, что неэффективно. Лучше использовать поиск напрямую, чтобы вы могли кэшировать свой результат, например так:Конечно, если вы не заботитесь об эффективности, вы всегда можете свернуть свои собственные, но в этом случае вам, вероятно, не следует использовать C ++ ...;)
источник
list::remove
,remove(makes_sense_only_for_vector, iterators)
...)В C ++ 20 мы наконец получим
std::set::contains
метод.источник
Если вы собираетесь добавить
contains
функцию, она может выглядеть так:Это работает с
std::set
другими контейнерами STL и даже массивами фиксированной длины:Редактировать:
Как указано в комментариях, я непреднамеренно использовал функцию, новую для C ++ 0x (
std::begin
иstd::end
). Вот почти тривиальная реализация VS2010:источник
std::set
, и помните, что это уместно, только если единственное, что вам нужно знать, это существование.Вы также можете проверить, находится ли элемент в наборе или нет при вставке элемента. Версия с одним элементом возвращает пару, в которой для ее элемента pair :: first установлено значение итератора, указывающее либо на вновь вставленный элемент, либо на эквивалентный элемент, уже находящийся в наборе. Элемент pair :: second в паре устанавливается равным true, если новый элемент был вставлен, или false, если эквивалентный элемент уже существует.
Например: предположим, что в наборе уже есть 20 в качестве элемента.
Если элемент недавно вставлен, то pair :: first будет указывать на позицию нового элемента в наборе.
источник
Напишите свой собственный:
источник
я использую
Но это не так эффективно, как
Моя версия только экономит мое время при написании кода. Я предпочитаю это так для конкурентного кодирования.
источник
count()
. Любой, кто не в состоянии понять, что функция, возвращающая целое число, используемая в булевом выражении, проверяет ненулевое, будет иметь много, много других проблем в большом море идиом C / C ++. И, как отмечалось выше, действительно должно быть максимально эффективно для наборов, о чем и шла речь.Я был в состоянии написать общую
contains
функцию дляstd::list
иstd::vector
,Это немного очищает синтаксис.
Но я не мог использовать магию параметра шаблона шаблона, чтобы заставить эту работу работать с произвольными stl-контейнерами.
Любые комментарии об улучшении последнего ответа были бы хорошими.
источник
template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
// общий синтаксис
/ * в приведенном ниже коде я пытаюсь найти элемент 4 и установить int, если он присутствует или нет * /
источник