IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

SL & STL C++ Discussion :

A propos du dernier élément d'un vector<>


Sujet :

SL & STL C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut A propos du dernier élément d'un vector<>
    Le dernier élément d'un vector<> peut s'obtenir par deux écritures On est tous d'accord je suppose que c'est la même chose... enfin presque, parce que v.end()-1 est de type iterator alors que v.rbegin() est de type reverse_iterator.

    Mon soucis intervient lorsque je veut retirer le dernier élément d'un vector.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    v.erase(v.end()-1)
    v.erase(v.rbegin())
    Le compilateur refuse la deuxième expression, c'est assez dommage je trouve (VC++6).

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Par défaut
    ça devrait être possible en utilisant la méthode .base() du reverse_iterator:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    v.erase( v.rbegin().base() );
    <EDIT> et en fait non c'est dangereux </EDIT>

    Rien n'indique que iterator et reverse_iterator se doivent d'être du même type, donc .base() te renvoie l'iterator correspondant à ton reverse_iterator.

    euh sinon, pour faire simple:

  3. #3
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    J'en apprend tous les jours...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    void pop_back()
    	{erase(end() - 1); }
    Merci !

  4. #4
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Par défaut
    Citation Envoyé par nikko34 Voir le message
    Rien n'indique que iterator et reverse_iterator se doivent d'être du même type,
    Tout indique qu'ils ne peuvent pas être du même type!

    Citation Envoyé par nikko34 Voir le message
    .base() te renvoie l'iterator correspondant à ton reverse_iterator.
    base() renvoie l'itérateur stocké par le reverse_iterator, celui qui a été passé en argument au constructeur :
    reverse_iterator<I>(i).base() = i

    base() ne renvoie pas un itérateur correspondant dans le sens où tu l'entends : pointant sur le même objet.

    Citation Envoyé par nikko34 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    v.erase( v.rbegin().base() );
    Non surtout pas! Par définition :
    v.rbegin() = V::reverse_iterator(v.end())
    où V est le type de v, donc
    v.rbegin().base() = v.end()
    Par définition, v.end() ne pointe pas sur un élément, et donc v.erase(v.rbegin().base()) a un comportement indéfini.

    Pour le principe de fonctionnement de reverse_iterator et la relation entre *r et r.base(), je te conseille de lire une doc sur la STL.

    Pour un exemple de code utilisant les propriétés de reverse_iterator et notamment base(), regarde mon message #4.

  5. #5
    Membre émérite
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Par défaut
    ouaip, tu as raison, je me suis encore fait avoir par le erase( end ) et gcc.

    C'est juste la deuxième fois que je fais la même erreur ici. Ca doit vouloir dire quelque chose, je dois faire un blocage là dessus.

    sinon, pop_back(), c'est bien.

  6. #6
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 035
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 035

  7. #7
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Par défaut
    Citation Envoyé par nikko34 Voir le message
    ouaip, tu as raison, je me suis encore fait avoir par le erase( end ) et gcc.

    C'est juste la deuxième fois que je fais la même erreur ici. Ca doit vouloir dire quelque chose, je dois faire un blocage là dessus.
    Tu utilises le mode de débogage de la STL?

  8. #8
    Membre émérite
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Par défaut
    Citation Envoyé par corrector Voir le message
    Tu utilises le mode de débogage de la STL?
    Je ne pense pas, j'utilise mon GCC sans rien de spécial, sous SUSE10.

    Est-ce que ça peut être considéré comme un bug? Vu que le comportement est indéfini, tout peut se passer...

    Effectivement VisualC++ crashe avec le même code. J'aurais pu éviter de raconter des bétises ( oui je teste quand même tout le code que je poste )

  9. #9
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Par défaut
    Citation Envoyé par camboui Voir le message
    Le dernier élément d'un vector<> peut s'obtenir par deux écritures On est tous d'accord je suppose que c'est la même chose...
    Non, justement, en dehors du fait que :
    &*(v.end()-1) == &*v.rbegin()
    le comportement n'est pas le même.

    Citation Envoyé par camboui Voir le message
    enfin presque, parce que v.end()-1 est de type iterator alors que v.rbegin() est de type reverse_iterator.

    Mon soucis intervient lorsque je veut retirer le dernier élément d'un vector.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    v.erase(v.end()-1)
    v.erase(v.rbegin())
    Le compilateur refuse la deuxième expression, c'est assez dommage je trouve (VC++6).
    L'interface des conteneurs est exprimée en terme de iterator, pas de reverse_iterator : insert et erase prennent des iterator; dans les conteneurs associatifs, find renvoie un iterator, etc.

    En réalité, les reverse_iterator ne sont pas du tout intégrés aux conteneurs; reverse_iterator c'est juste un adaptateur d'itérateur. Il n'y avait aucun besoin de demander aux conteneurs d'implémenter rbegin, rend, alors que c'est juste reverse_iterator(end()) et reverse_iterator(begin()) respectivement, on aurait aussi bien pu définir quatre fonctions libre (non testé) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    namespace std {
    template <class Container>
    reverse_iterator<typename Container::iterator> rbegin (Container& c) {
        return reverse_iterator<typename Container::iterator> (c.end ());
    }
     
    template <class Container>
    reverse_iterator<typename Container::iterator> rend (Container& c) {
        return reverse_iterator<typename Container::iterator> (c.begin());
    }
     
    template <class Container>
    reverse_iterator<typename Container::const_iterator> rbegin (const Container& c) {
        return reverse_iterator<typename Container::const_iterator> (c.end ());
    }
     
    template <class Container>
    reverse_iterator<typename Container::const_iterator> rend (const Container& c) {
        return reverse_iterator<typename Container::const_iterator> (c.begin());
    }
    } // std
    Et voilà, pas la peine d'embêter les auteurs de conteneurs, le service rendu est le même, et ça montre flexibilité de la conception par adaptateur : imposer la présence systématique d'un adaptateur dans chaque classe, c'est vraiment de l'anti-conception.

    De plus, l'implémentation avec des fonctions libres évite de donner l'impression que les reverse_iterator font partie de l'interface des conteneurs dans le sens d'une véritable intégration.

    En fait, rien n'empêche de faire ce que tu veux avec des fonctions libres (non testé) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <class Container>
    reverse_iterator<typename Container::iterator> 
    insert (Container& c, reverse_iterator<typename Container::iterator> i) {
        return reverse_iterator<typename Container::iterator> (next (c.insert (i.base())));
    }
     
    template <class Container>
    reverse_iterator<typename Container::iterator> 
    erase (Container& c, reverse_iterator<typename Container::iterator> i) {
        return reverse_iterator<typename Container::iterator> (c.erase (prev (i.base())));
    }
    Cependant, les règles d'invalidation sont complètement différentes de ce qu'on a d'habitude; en effet, avec une liste (std::list), un reverse_iterator vers un élément n'est pas invalidé par la suppression de cet élément :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // définitions, initialisation : 
    typedef list<int> li_t;
    lit_t li;
    li.push_back (1); li.push_back (2);
    typedef lit_t::reverse_iterator rev_t;
    rev_t r = li.rbegin ();
     
    // test :
    cout << *r; // -> 2
    r2 = erase (li, r);
    cout << *r2; // -> 1
    cout << *r; // -> 1 !!!
    cout << r == r2; // -> 1
    parce que dans ce cas (std::list), erase renvoie son argument. Mais attention à ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    // définitions, initialisation : pareil 
    // test : 
    rev_t r2 = next (r);
    cout << *r2; // -> 1
    erase (li, r);
    cout << *r2; // NON : comportement indéfini, r2 est invalidé
    En effet, r2 pointait sur l'élément 1 parce que r2.base() pointait sur l'élément 2; celui-ci ayant été supprimé, r2 est invalidé, tandis que r, qui a été passé à ::erase, est toujours valide et pointe sur l'élément 1, comme r2 avant! Ce qui est tout à fait logique, parce que r.base() pointe un-après-la-fin, que ce soit avant ou après le ::erase.

    Il y aura aussi des surprises avec ::insert si on s'attend à une sémantique équivalente à celles des iterator et de list::insert.

    L'utilisateur doit bien comprendre ces subtilités, subtilités causées par la définition de reverse_iterator en terme de l'itérateur base() décalé d'un élément, ce qui est tout sauf un détail d'implémentation quand on change le contenu du conteneur.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Copie dernier élément de chaque type, sur la feuille suivante
    Par baptbapt dans le forum Macros et VBA Excel
    Réponses: 33
    Dernier message: 26/07/2006, 09h59
  2. Réponses: 10
    Dernier message: 09/06/2006, 17h02
  3. [PL/SQL] Curseur - Récupération du dernier élément
    Par dupont166 dans le forum Oracle
    Réponses: 5
    Dernier message: 27/12/2005, 04h57
  4. for-each et dernier élément
    Par neptune dans le forum XSL/XSLT/XPATH
    Réponses: 2
    Dernier message: 08/09/2005, 14h53
  5. Comment récupérer l'index du dernier élément inséré ?
    Par Didier100 dans le forum Bases de données
    Réponses: 4
    Dernier message: 15/07/2004, 22h41

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo