clase - show set c++
La actualizaciĆ³n del conjunto C++ STL es tediosa: no puedo cambiar un elemento en su lugar (7)
Encuentro que la operación de actualización es tediosa ya que no existe dicha API en cppreference . Entonces, lo que hago actualmente es algo así:
//find element in set by iterator
Element copy = *iterator;
... // update member value on copy, varies
Set.erase(iterator);
Set.insert(copy);
Básicamente, el retorno del iterador por parte de Set es un const_iterator y no puede cambiar su valor directamente.
¿Hay una mejor manera de hacer esto? O tal vez debería anular el conjunto creando el mío (que no sé exactamente cómo funciona ...)
Es posible que desee utilizar un std :: map en su lugar. Use la porción de Elemento que afecta el orden de la clave, y ponga todo Elemento como el valor. Habrá alguna duplicación de datos menor, pero tendrá actualizaciones más fáciles (y posiblemente más rápidas).
Esto es más rápido en algunos casos:
std::pair<std::set<int>::iterator, bool> result = Set.insert(value);
if (!result.second) {
Set.erase(result.first);
Set.insert(value);
}
Si el valor generalmente no está ya en el conjunto estándar, esto puede tener un mejor rendimiento.
Hay 2 formas de hacerlo, en el caso fácil:
- Puede usar
mutable
en la variable que no es parte de la clave - Puede dividir su clase en un par de
Value
Key
(y usar unstd::map
)
Ahora, la pregunta es para el caso complicado: ¿qué sucede cuando la actualización realmente modifica la parte key
del objeto? Tu enfoque funciona, aunque admito que es tedioso.
Me encontré con el mismo problema en C ++ 11, donde de hecho ::std::set<T>::iterator
es constante y por lo tanto no permite cambiar su contenido, incluso si sabemos que la transformación no afectará a <
invariant . Puede mutable_set
esto al envolver ::std::set
en un tipo mutable_set
o escribir un contenedor para el contenido:
template <typename T>
struct MutableWrapper {
mutable T data;
MutableWrapper(T const& data) : data(data) {}
MutableWrapper(T&& data) : data(data) {}
MutableWrapper const& operator=(T const& data) { this->data = data; }
operator T&() const { return data; }
T* operator->() const { return &data; }
friend bool operator<(MutableWrapper const& a, MutableWrapper const& b) {
return a.data < b.data;
}
friend bool operator==(MutableWrapper const& a, MutableWrapper const& b) {
return a.data == b.data;
}
friend bool operator!=(MutableWrapper const& a, MutableWrapper const& b) {
return a.data != b.data;
}
};
Esto me parece mucho más simple y funciona en el 90% de los casos sin que el usuario siquiera note que hay algo entre el conjunto y el tipo real.
Si su conjunto contiene objetos que desea alterar, asegúrese de guardar sus punteros en el conjunto.
Bueno, esto no evita que insertes varios objetos con el mismo valor (bueno, no puedes insertar el mismo objeto varias veces) pero es útil si quieres un contenedor con inserción y eliminación rápidas.
set
devuelve const_iterators
(el estándar dice que set<T>::iterator
es const
, y que set<T>::const_iterator
y set<T>::iterator
pueden de hecho ser del mismo tipo - ver 23.2.4 / 6 en n3000 .pdf) porque es un contenedor ordenado. Si devolviera un iterator
regular, se le permitiría cambiar el valor de los elementos desde debajo del contenedor, lo que podría alterar el orden.
Su solución es la forma idiomática de alterar elementos en un set
.
Actualización: aunque a partir de ahora se cumple lo siguiente, el comportamiento se considera un defect y se modificará en la próxima versión del estándar. Qué triste.
Hay varios puntos que hacen que su pregunta sea bastante confusa.
- Las funciones pueden devolver valores, las clases no pueden.
std::set
es una clase, y por lo tanto no puede devolver nada. - Si puede llamar a
s.erase(iter)
, entoncesiter
no es unconst_iterator
.erase
requiere un iterador sin const. - Todas las funciones miembro de
std::set
que devuelven un iterador devuelven un iterador no constante siempre que el conjunto no sea const también.
Puede cambiar el valor de un elemento de un conjunto siempre que la actualización no cambie el orden de los elementos. El siguiente código compila y funciona muy bien.
#include <set>
int main()
{
std::set<int> s;
s.insert(10);
s.insert(20);
std::set<int>::iterator iter = s.find(20);
// OK
*iter = 30;
// error, the following changes the order of elements
// *iter = 0;
}
Si su actualización cambia el orden de los elementos, entonces debe borrarlos y reinsertarlos.