example - Estándar de C++: const_iterator inesperado en multiset
multiset c++ (2)
Cómo engañar al compilador para std :: set :: iterator?
Tengo estructura
struct _item {
int a;
int b;
bool operator <(const _item& x) const {return a<x.a;}
};
Quiero cambiar solo el miembro b (b es irrelevante para ordenar en conjunto, solo se compara el miembro a).
std::set<_item> data;
std::set<_item>::iterator iter=data.begin();
iter->b=0; // error !!!
Avada Kedavra!
struct _item {
int a;
int b;
_item* self;
_item() {self=this;}
bool operator <(const _item& x) const {return a<x.a;}
};
iter->self->b=0; // Success !! Tested on VC10
Por supuesto, más C + + correctamente
struct _item {
int a;
int b;
private:
_item* self;
public:
_item() {self=this;}
bool operator <(const _item& x) const {return a<x.a;}
int& bReference() const {return self->b;}
};
std::set<_item> items;
std::set<_item>::iterator iter=items.begin();
iter->bReference()=0; // Success !! Tested on VC1
Recientemente me encontré con un problema extraño en el que obtendría un const_iterator
lugar del iterator
esperado al iterar a través de un multiset. Resultó ser un problema para MSVC, pero g ++ me dio un error:
error: inicialización no válida de la referencia del tipo ''myPtr &'' de la expresión del tipo ''const boost :: shared_ptr''
Código relevante:
typedef std::multiset<myPtr> myList;
myList _mystuff;
void tick(float dt)
{
for (myList::iterator i = _mystuff.begin(); i != _mystuff.end(); ++i)
{
myPtr &mine = *i; // g++ problem here, not for MSVC
// const myPtr &mine = *i; works fine for g++
mine->tick(dt);
}
}
Una gran cantidad de investigación reveló que es un problema con mucha discusión previa. Encontré estos bits relevantes:
- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14990
- http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#322
- http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#103
- http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#279
- http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#528
Mi conocimiento de fondo y comprensión sobre el tema es limitado y, por lo tanto, me gustaría saber si el estándar no define este comportamiento lo suficientemente bien en cuyo caso g ++ y MSVC implementan el comportamiento a su gusto o si g ++ o MSVC se desvían de un estándar bien definido.
Gracias por adelantado.
Los iteradores para set y multiset se cambiaron del par de iterador / iterador estándar a ser solo iteradores. La razón de este cambio fue que son contenedores ordenados, y cambiar el elemento dentro de un iterador puede invalidar esta restricción de ordenamiento.
La versión de GCC con la que está probando ha hecho este cambio, la versión de VC que está utilizando no. VC10 (y VC9 SP1, creo) siempre devuelven const_iterators de conjuntos y multisedes.
23.2.4 / 6 del último borrador de C ++ 1x (n3000.pdf en este momento) dice
Para los contenedores asociativos donde el tipo de valor es el mismo que el tipo de clave, tanto iterator como const_iterator son iteradores constantes.
std :: set y std :: multi_set son los contenedores asociativos donde el tipo de valor es el mismo que el tipo de clave.