Lo uso mucho std::set<int>y, a menudo, simplemente necesito verificar si dicho conjunto contiene un número o no.
Me resultaría natural escribir:
if (myset.contains(number))
...
Pero debido a la falta de un containsmiembro, necesito escribir lo engorroso:
if (myset.find(number) != myset.end())
..
o lo no tan obvio:
if (myset.count(element) > 0)
..
¿Hay alguna razón para esta decisión de diseño?

count()enfoque es que hace más trabajo del quecountains()tendría que hacer.contains()el que devuelve unaboolsería perder información valiosa sobre los que el elemento está en la colección .find()conserva y devuelve esa información en forma de iterador, por lo que es una mejor opción para una biblioteca genérica como STL. (Sinbool contains()embargo, eso no quiere decir que no sea muy agradable de tener o incluso necesario)contains(set, element)función gratuita utilizando la interfaz pública del conjunto. Por tanto, la interfaz del conjunto es funcionalmente completa; agregar un método de conveniencia solo aumenta la interfaz sin habilitar ninguna función adicional, que no es la forma de C ++.Respuestas:
Creo que probablemente fue porque estaban tratando de hacer
std::sety lostd::multisetmás similar posible. (Y obviamentecounttiene un significado perfectamente sensato parastd::multiset).Personalmente creo que esto fue un error.
No se ve tan mal si finge que
countes solo un error ortográficocontainsy escribe la prueba como:Sin embargo, sigue siendo una pena.
fuente
.end()).contains(), debido a que sería redundante porque para cualquierstd::set<T> syT t, el resultado des.contains(t)es exactamente idéntico al resultado destatic_cast<bool>(s.count(t)). Como el uso del valor en una expresión condicional lo convertiría implícitamente enbool, es posible que hayan sentido quecount()sirvió bastante bien para el propósito.if (myset.ICanHaz(element)) ...: DPara poder escribir
if (s.contains()),contains()tiene que devolver unbool(o un tipo convertible abool, que es otra historia), comobinary_searchhace.La razón fundamental detrás de la decisión de diseño de no hacerlo de esta manera es que, si se
contains()devuelve,boolse perdería información valiosa sobre dónde se encuentra el elemento en la colección .find()conserva y devuelve esa información en forma de iterador, por lo que es una mejor opción para una biblioteca genérica como STL. Este ha sido siempre el principio rector de Alex Stepanov, como ha explicado a menudo (por ejemplo, aquí ).En cuanto al
count()enfoque en general, aunque a menudo es una buena solución, el problema es que hace más trabajo del quecontains()tendría que hacer .Eso no quiere decir que
bool contains()no sea muy agradable tener o incluso necesario. Hace un tiempo tuvimos una larga discusión sobre este mismo tema en el grupo ISO C ++ Standard - Future Proposals.fuente
contains(), obviamente, interactuaría fuertemente con los contenedores y algoritmos existentes, que se verían fuertemente afectados por conceptos y rangos, en el momento que se esperaba que entraran en C ++ 17, y Estaba convencido (como resultado de la discusión y de un par de intercambios de correos electrónicos privados) de que era una mejor idea esperarlos primero. Por supuesto, en 2015 no estaba claro que ni los conceptos ni los rangos no llegarían a C ++ 17 (de hecho, había muchas esperanzas de que lo hicieran). Sin embargo, no estoy seguro de que valga la pena seguirlo ahora.std::set(que es sobre lo que pregunta la pregunta), no veo cómocountfunciona más decontainslo que tendría que hacer. La implementación glibc decountes (aproximadamente)return find(value) == end() ? 0 : 1;. Aparte de los detalles sobre el operador ternario frente al regreso!= end()(que esperaría que el optimizador elimine), no veo cómo hay más trabajo.myset.contains()(si existiera), sería una indicación bastante fuerte de que esa información no es valiosa ( al usuario en ese contexto).count()más trabajo del quecontains()debería hacerstd::set? Es único, por lo quecount()podría serreturn contains(x) ? 1 : 0;exactamente lo mismo.Le falta porque nadie lo agregó. Nadie lo agregó porque los contenedores del STL que
stdincorporó la biblioteca estaban diseñados para ser mínimos en la interfaz. (Tenga en cuenta questd::stringno provino de la STL de la misma manera).Si no le importa una sintaxis extraña, puede fingirla:
utilizar:
Básicamente, puede escribir métodos de extensión para la mayoría de los
stdtipos de C ++ utilizando esta técnica.Tiene mucho más sentido hacer esto:
pero me divierte el método del método de extensión.
Lo realmente triste es que escribir un eficiente
containspodría ser más rápido en unmultimapomultiset, ya que solo tienen que encontrar un elemento, mientras quecounttienen que encontrar cada uno de ellos y contarlos .Un multiset que contenga mil millones de copias de 7 (ya sabes, en caso de que te quedes sin) puede tener una muy lenta
.count(7), pero podría tener una muy rápidacontains(7).Con el método de extensión anterior, podríamos hacerlo más rápido para este caso usando
lower_bound, comparandoendy luego comparando con el elemento. Sin embargo, hacer eso para un maullido desordenado y ordenado requeriría un SFINAE elegante o sobrecargas específicas del contenedor.fuente
std::setque no puede contener duplicados y, porstd::set::countlo tanto , siempre volveré0o1.std::multiset::countcanbackticksalrededor de la palabra "conjunto" se debe a que no me refierostd::setespecíficamente. Para que se sienta mejor,Estás mirando un caso particular y no ves un panorama más amplio. Como se indica en la documentación,
std::setcumple con los requisitos del concepto AssociativeContainer . Para ese concepto no tiene ningún sentido tener uncontainsmétodo, ya que es bastante inútil parastd::multisetystd::multimap, perocountfunciona bien para todos ellos. Aunque el métodocontainspodría añadirse como un alias paracountparastd::set,std::mapy sus versiones hash (comolengthporsize()enstd::string), pero se parece a los creadores de la biblioteca no vio necesidad real para ello.fuente
stringes un monstruo: existía antes del STL, donde teníalengthy todos esos métodos que están basados en índices, y luego se "contenedor" para encajar dentro del modelo STL ... sin eliminar los métodos existentes por razones de compatibilidad con versiones anteriores . Ver GotW # 84: Monoliths Unstrung =>stringrealmente viola el principio de diseño de "cantidad mínima de funciones miembro".containses igual en esfuerzo en un set / mapa, pero se puede hacer más rápido quecounten un multiset / multimap.containsmétodo.size()yempty()son duplicados, pero muchos contenedores tienen ambos.Aunque no sé por qué
std::setno tienecontainsperocountque solo regresa0o1, puede escribir unacontainsfunción de ayuda con plantilla como esta:Y utilícelo así:
fuente
containsmétodo existe, simplemente se nombra de una manera estúpida.std::stringcoughstd.::string¡NO es parte de STL! Es parte de la biblioteca estándar y se diseñó retroactivamente ...countpuede ser más lento ya que efectivamente necesita hacer dosfinds para obtener el comienzo y el final del rango si se comparte el código conmultiset.contains. No veo nada malo con eso. @MarkRansom, el pequeño SFINAE es para evitar que esta plantilla se vincule a cosas que no debería.La verdadera razón
setes un misterio para mí, pero una posible explicación para este mismo diseñomappodría ser evitar que las personas escriban código ineficiente por accidente:Lo que resultaría en dos
mapbúsquedas.En cambio, se ve obligado a obtener un iterador. Esto le da una pista mental de que debe reutilizar el iterador:
que consume solo una
mapbúsqueda.Cuando nos damos cuenta de eso
setymapestamos hechos de la misma carne, podemos aplicar este principio también aset. Es decir, si queremos actuar sobre un elementosetsolo si está presente en elset, este diseño puede impedirnos escribir código como este:Por supuesto, todo esto es una mera especulación.
fuente
if (myMap.count("Meaning of universe"))muy bien, entonces ...?containsque concount.Desde c ++ 20,
bool contains( const Key& key ) constestá disponible.
fuente
¿Qué pasa con binary_search?
fuente
std::unordered_set, perostd::setsí lo haría.contiene () tiene que devolver un bool. Usando el compilador C ++ 20 obtengo el siguiente resultado para el código:
fuente
Otra razón es que le daría al programador la falsa impresión de que std :: set es un conjunto en el sentido de la teoría matemática de conjuntos. Si implementan eso, seguirían muchas otras preguntas: si un std :: set tiene contiene () para un valor, ¿por qué no lo tiene para otro conjunto? ¿Dónde están union (), intersection () y otras operaciones de conjunto y predicados?
La respuesta es, por supuesto, que algunas de las operaciones de conjunto ya están implementadas como funciones en (std :: set_union (), etc.) y otras están implementadas de manera tan trivial como contains (). Las funciones y los objetos de función funcionan mejor con abstracciones matemáticas que los miembros de objeto, y no se limitan al tipo de contenedor en particular.
Si uno necesita implementar una funcionalidad completa de un conjunto matemático, no solo tiene la opción de un contenedor subyacente, sino que también tiene una opción de detalles de implementación, por ejemplo, ¿funcionaría su función theory_union () con objetos inmutables, más adecuada para la programación funcional? , ¿o modificaría sus operandos y ahorraría memoria? ¿Se implementaría como objeto de función desde el principio o sería mejor implementar una función C y usar std :: function <> si es necesario?
Como está ahora, std :: set es solo un contenedor, muy adecuado para la implementación de set en sentido matemático, pero está tan lejos de ser un conjunto teórico como std :: vector de ser un vector teórico.
fuente