2009-06-20 5 views
4

Si la valeur d'un élément dans un ensemble change, l'ordre peut ne plus être correct. Comme illustré dans ce petit programme:Comment dire à std :: set de 'rafraîchir' sa commande?

#include <algorithm> 
#include <iostream> 
#include <set> 
#include <string> 

struct Comp 
{ 
    bool operator()(const std::string * lhs, const std::string * rhs) 
    { 
     return *lhs < *rhs; 
    } 
}; 

int main() 
{ 
    typedef std::set<std::string*, Comp> MySet; 
    MySet mySet; 

    std::string * a = new std::string("a"); 
    mySet.insert(a); 

    std::string * c = new std::string("c"); 
    mySet.insert(c); 

    std::string * b = new std::string("b"); 
    mySet.insert(b); 

    for (MySet::iterator it = mySet.begin(); it != mySet.end(); ++it) 
    { 
     std::cout << *(*it) << std::endl; 
    } 

    // Ouput has correct order: 
    // a 
    // b 
    // c 


    *b = "z"; 
    std::cout << std::endl; 

    std::string * d = new std::string("d"); 
    mySet.insert(d);  

    for (MySet::iterator it = mySet.begin(); it != mySet.end(); ++it) 
    { 
     std::cout << *(*it) << std::endl; 
    } 

    // Output no longer ordered correctly: 
    // a 
    // d 
    // z 
    // c 

    return 0; 
} 

Comment puis-je dire à l'ensemble de «rafraîchir» son tri interne?

+0

La valeur ne doit pas changer. Le 'value_type' devrait être' std :: set '(bien que je crois que VS ne respecte pas cela?) –

+0

Le type de valeur est ce que l'utilisateur passe, avec un const de niveau supérieur ajouté. Si l'utilisateur passe 'std :: string *', le type de valeur sera 'std :: string * const'. Il n'y a pas de règle interdisant à l'utilisateur de passer quelque chose qui n'applique pas l'immutabilité de la position de tri de la valeur; il y a juste une règle qui dit que modifier la valeur de telle manière engendre un comportement indéfini. –

Répondre

9

sujet très similaire ici (mais pas tout à fait un doublon, parce que vous stockez des pointeurs vers des objets mutables avec une comparaison personnalisée):

what happens when you modify an element of an std::set?

En fait, ne pas faire ce que vous essayez faire. Au lieu de cela, lorsque vous voulez modifier un objet sur lequel set contient un pointeur, supprimez d'abord le pointeur, puis modifiez l'objet, puis réinsérez le pointeur.

5

Simplement, vous ne pouvez pas. Si vous placez un objet dans un ensemble, vous ne devez pas modifier l'élément d'une manière qui modifie sa commande. Si vous devez modifier un élément de cette manière, vous devez le supprimer de l'ensemble (set :: erase) et réinsérer un nouvel élément (std :: insert) avec la nouvelle valeur.

1

Il est à noter que si vous utilisez vs 2008, l'implémentation std::set prend en charge les itérateurs non constants, ce qui rend le code que vous décrivez compilé avec succès en utilisant cette bibliothèque. Dans d'autres implémentations stl (par exemple sgi's), set::const_iterator et set::iterator sont du même type qui se plaindraient de définir explicitement une nouvelle valeur de clé. Copiez-le lui-même en utilisant un prédicat de comparaison différent.

+1

Les itérateurs sont const dans la norme. Si vous essayez de mettre à jour les membres d'un ensemble via un itérateur, vous devriez obtenir des erreurs de compilation. – jkp

0

Habituellement, ceci est utilisé pour spécifier une opération de comparaison différente, par exemple pour le trier en utilisant une partie différente d'une classe/struct stockée.

+0

NB. Ce sera beaucoup plus cher que de supprimer un seul élément, de le modifier, puis de le rajouter. –

+0

Si cela conduirait à un changement dans MySet, c'est déjà comportement non défini – sehe

+0

'std :: set <...> MySet();' est ** pas ** variable .. c'est une déclaration de fonction !! – ikh

2

Si la valeur d'un élément dans un ensemble change

Stop! Cela ne peut pas se produire légalement.

std::set ne fournit aucun moyen de faire ce que vous demandez, car il est déjà un pré-requis qu'une ré-instruction manuelle ne sera jamais nécessaire.

Questions connexes