4 votes

Quel est un équivalent sûr de l'effacement STL non-vide ?

Supposons que j'ai un hash_map et un code tel que

// i is an iterator
i = hash_map.erase(i)

Mais la STL de GCC ne retourne pas d'itérateur dans l'effacement, mais un void. Maintenant, un code comme

hash_map.erase(i++)

sûre (c'est-à-dire qu'elle n'invalide pas l'itérateur ou ne fait pas d'autres choses inattendues ou désagréables) ? Veuillez noter qu'il s'agit d'un hash_map.

6voto

Chris Jester-Young Points 102876

Oui, c'est sûr, car la valeur de i aura été réglé sur la valeur suivante, avant que la valeur actuelle ne soit effacée.

Selon le Documentation SGI sur les conteneurs hachés L'invalidation ne se produit pas pour les éléments non effacés, ni même pour les redimensionnements (on ne sait pas si les insertions provoquent des redimensionnements, donc par prudence j'admets cette possibilité) -- mais dans ce dernier cas, l'ordre d'itération sera modifié. Mais cela ne s'applique pas ici, à moins que vous ne fassiez tout pour redimensionner le conteneur pendant la traversée ou autre chose :-)

2voto

Vous pouvez encapsuler l'effacement afin de fournir la même interface pour tous les conteneurs que vous utilisez :

namespace detail {
template<typename Container, typename R>
struct SelectErase {
  // by default, assume the next iterator is returned
  template<typename Iterator>
  Iterator erase(Container& c, Iterator where) {
    return c.erase(where);
  }
};
// specialize on return type void
template<typename Container>
struct SelectErase<Container, void> {
  template<typename Iterator>
  Iterator erase(Container& c, Iterator where) {
    Iterator next (where);
    ++next;
    c.erase(where);
    return next;
  }
};

template<typename I, typename Container, typename R>
SelectErase<Container,R> select_erase(R (Container::*)(I)) {
  return SelectErase<Container,R>();
}
} // namespace detail

template<typename Container, typename Iterator>
Iterator erase(Container& container, Iterator where) {
  return detail::select_erase<Iterator>(&Container::erase).erase(container, where);
}

Cela nécessite soit :

  1. c.erase retourne l'itérateur pour l'élément suivant. C'est ainsi que fonctionnent les vecteurs, les deques et les listes.
  2. c.erase retourne void et n'invalide pas l'itérateur suivant. C'est ainsi que map, set, et (non-stdlib) hash_map fonctionnent.

-4voto

Rodyland Points 349

Je ne veux pas casser l'ambiance, mais je ne pense pas que ce que vous proposez soit sûr.

i++ est l'opérateur de post-incrémentation, ce qui signifie que i est incrémenté après l'appel à l'effacement. Mais l'effacement invalide tous les itérateurs pointant vers l'élément qui est effacé. Donc au moment où i est incrémenté, il n'est plus valide.

Si vous avez de la chance, cela peut fonctionner correctement par accident jusqu'au jour où cela ne fonctionne plus.

Pour autant que je sache, il n'y a pas de moyen de contourner ce problème, mais quelque chose du genre :

// tmp and i are both iterators
tmp = i;
++i;
hash_map.erase(tmp);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X