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 contains
miembro, 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 unabool
serí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::set
y lostd::multiset
más similar posible. (Y obviamentecount
tiene un significado perfectamente sensato parastd::multiset
).Personalmente creo que esto fue un error.
No se ve tan mal si finge que
count
es solo un error ortográficocontains
y escribe la prueba como:Sin embargo, sigue siendo una pena.
fuente
.end()
).contains()
, debido a que sería redundante porque para cualquierstd::set<T> s
yT 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_search
hace.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,bool
se 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ómocount
funciona más decontains
lo que tendría que hacer. La implementación glibc decount
es (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
std
incorporó la biblioteca estaban diseñados para ser mínimos en la interfaz. (Tenga en cuenta questd::string
no 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
std
tipos 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
contains
podría ser más rápido en unmultimap
omultiset
, ya que solo tienen que encontrar un elemento, mientras quecount
tienen 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
, comparandoend
y 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::set
que no puede contener duplicados y, porstd::set::count
lo tanto , siempre volveré0
o1
.std::multiset::count
canbackticks
alrededor de la palabra "conjunto" se debe a que no me refierostd::set
especí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::set
cumple con los requisitos del concepto AssociativeContainer . Para ese concepto no tiene ningún sentido tener uncontains
método, ya que es bastante inútil parastd::multiset
ystd::multimap
, perocount
funciona bien para todos ellos. Aunque el métodocontains
podría añadirse como un alias paracount
parastd::set
,std::map
y sus versiones hash (comolength
porsize()
enstd::string
), pero se parece a los creadores de la biblioteca no vio necesidad real para ello.fuente
string
es un monstruo: existía antes del STL, donde teníalength
y 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 =>string
realmente viola el principio de diseño de "cantidad mínima de funciones miembro".contains
es igual en esfuerzo en un set / mapa, pero se puede hacer más rápido quecount
en un multiset / multimap.contains
método.size()
yempty()
son duplicados, pero muchos contenedores tienen ambos.Aunque no sé por qué
std::set
no tienecontains
perocount
que solo regresa0
o1
, puede escribir unacontains
función de ayuda con plantilla como esta:Y utilícelo así:
fuente
contains
método existe, simplemente se nombra de una manera estúpida.std::string
coughstd.::string
¡NO es parte de STL! Es parte de la biblioteca estándar y se diseñó retroactivamente ...count
puede ser más lento ya que efectivamente necesita hacer dosfind
s 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
set
es un misterio para mí, pero una posible explicación para este mismo diseñomap
podría ser evitar que las personas escriban código ineficiente por accidente:Lo que resultaría en dos
map
bú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
map
búsqueda.Cuando nos damos cuenta de eso
set
ymap
estamos hechos de la misma carne, podemos aplicar este principio también aset
. Es decir, si queremos actuar sobre un elementoset
solo 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 ...?contains
que concount
.Desde c ++ 20,
bool contains( const Key& key ) const
está disponible.
fuente
¿Qué pasa con binary_search?
fuente
std::unordered_set
, perostd::set
sí 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