OVH Cloud OVH Cloud

Implementation end/rend pour list et map

19 réponses
Avatar
Marc Boyer
Bonjour,

dans le cadre d'unn projet en C, j'en suis ammené à m'interroger
sur l'implémentation des itérateurs end/rend dans list et map.

En effet, dans ces deux conteneurs, une impleméntation triviale d'un
itérateur est de faire un pointeur sur un élément interne (cellule
pour list et noeud pour map), avec une surcharge de * et ->.

Mais, comment coder end (et rend), en restant homogène ?
J'ai plusieurs idées en tête, mais la plus réaliste est aussi
la plus moche :-(

1) Ajouter des éléments end/rend
Ajouter dans la liste et dans l'arbre des éléments "en plus",
des cellules/noeuds marqueurs de fin. Mais autant c'est faisable
pour la liste (avec des tests genre it->next->next != NULL
au lieu de it->next != NULL), autant dans l'arbre, ça
complique tous les codes.

2) Itérateur comme tuple (pointeur element, pointeur conteneur)
Mettre dans un itérateur un pointeur sur le conteneur lui
même, ainsi, on peu imaginer que (NULL, p1) est le résultat
de p1->end().

A priori, je me dirige vers 2), surtout pour l'arbre de map,
mais j'ai peut-être raté un truc.

J'ai tenté de jetter un coup d'oeuil sur les sources
de la stl de gcc, et les itérateurs semblent n'etre qu'un
pointeur. Le list semble codé avec un element en rab, en
tête (?), end() étant l'adresse de ce pointeur, et
begin étant end()->next (en gros). Pour l'arbre, ça semble
être la racine, mais j'ai pas compris comment ils font si
par hasard on fait un erase() de cette racine...


Marc Boyer
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens...

10 réponses

1 2
Avatar
drkm
Marc Boyer writes:

dans le cadre d'unn projet en C, j'en suis ammené à m'interroger
sur l'implémentation des itérateurs end/rend dans list et map.

En effet, dans ces deux conteneurs, une impleméntation triviale d'un
itérateur est de faire un pointeur sur un élément interne (cellule
pour list et noeud pour map), avec une surcharge de * et ->.


Lorsque tu dis « faire un pointeur ... », j'imagine que tu veux dire
« définir une structure contenant un pointeur ... ». Il faut
également surcharger les opérateurs de comparaison, d'incrémentation
et de décrémentation.

Mais, comment coder end (et rend), en restant homogène ?


Qu'entends-tu par « homogène » ?

Ce qui me vient directement à l'esprit serait d'initialiser le
pointeur membre de l'itérateur à une valeur sentinelle, comme 0. Je
ne sais pas s'il s'agit d'une implémentation permise, mais je ne vois
pas a priori de contre-indication, ni d'avantage à avoir une autre
valeur.

J'ai plusieurs idées en tête, mais la plus réaliste est aussi
la plus moche :-(


Laquelle trouves-tu la plus réaliste (et la plus moche, donc) ?

1) Ajouter des éléments end/rend
Ajouter dans la liste et dans l'arbre des éléments "en plus",
des cellules/noeuds marqueurs de fin. Mais autant c'est faisable
pour la liste (avec des tests genre it->next->next != NULL
au lieu de it->next != NULL), autant dans l'arbre, ça
complique tous les codes.


Je ne vois pas l'intérêt.

2) Itérateur comme tuple (pointeur element, pointeur conteneur)
Mettre dans un itérateur un pointeur sur le conteneur lui
même, ainsi, on peu imaginer que (NULL, p1) est le résultat
de p1->end().


Il y a des avantages à avoir des itérateurs contenant une référence
vers le conteneur dont ils sont issus. Mais je pense que le modèle
des itérateurs de la STL ne permet pas de les exploiter.

A priori, je me dirige vers 2), surtout pour l'arbre de map,
mais j'ai peut-être raté un truc.

J'ai tenté de jetter un coup d'oeuil sur les sources
de la stl de gcc, et les itérateurs semblent n'etre qu'un
pointeur.


Huh ? Autant je comprendrais avec std::vector (et il me semble
qu'il s'agissait effectivement d'une implémentation courante), autant
je n'arrive pas à voir comment utiliser un simple pointeur comme
itérateur de std::list ou std::map.

Le list semble codé avec un element en rab, en
tête (?), end() étant l'adresse de ce pointeur, et
begin étant end()->next (en gros). Pour l'arbre, ça semble
être la racine,


L'itérateur renvoyé par end() désigne donc un noeud existant, mais
bidon ? On a donc une certaine identité entre chaque itérateur
retourné par end(), depuis une même liste ou map ...

mais j'ai pas compris comment ils font si
par hasard on fait un erase() de cette racine...


Tu parles bien de o.erase( o.end() ) ?!?

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html

Avatar
Marc Boyer
drkm wrote:
Marc Boyer writes:
En effet, dans ces deux conteneurs, une impleméntation triviale d'un
itérateur est de faire un pointeur sur un élément interne (cellule
pour list et noeud pour map), avec une surcharge de * et ->.


Lorsque tu dis « faire un pointeur ... », j'imagine que tu veux dire
« définir une structure contenant un pointeur ... ». Il faut
également surcharger les opérateurs de comparaison, d'incrémentation
et de décrémentation.


Tout a fait.

Mais, comment coder end (et rend), en restant homogène ?
Qu'entends-tu par « homogène » ?



Pas un horible cast de la mort pour mettre un pointeur
sur conteneur dans une variable destinée à recevoir un pointeur
sur noeud interne.

Ce qui me vient directement à l'esprit serait d'initialiser le
pointeur membre de l'itérateur à une valeur sentinelle, comme 0. Je
ne sais pas s'il s'agit d'une implémentation permise, mais je ne vois
pas a priori de contre-indication, ni d'avantage à avoir une autre
valeur.


C'était aussi mon idée, facile à implémenter, faite, sauf que,
comment faire end()-1 et retrouver begin()+(size()-1) ?

1) Ajouter des éléments end/rend
Ajouter dans la liste et dans l'arbre des éléments "en plus",
des cellules/noeuds marqueurs de fin. Mais autant c'est faisable
pour la liste (avec des tests genre it->next->next != NULL
au lieu de it->next != NULL), autant dans l'arbre, ça
complique tous les codes.


Je ne vois pas l'intérêt.


De end(), on passe facilement à end()-1

J'ai tenté de jetter un coup d'oeuil sur les sources
de la stl de gcc, et les itérateurs semblent n'etre qu'un
pointeur.


Huh ? Autant je comprendrais avec std::vector (et il me semble
qu'il s'agissait effectivement d'une implémentation courante), autant
je n'arrive pas à voir comment utiliser un simple pointeur comme
itérateur de std::list ou std::map.


Pointeur sur un noeud interne. Globalement, une list<T>, c'est
un list_node<T>* avec
class list_node<T> { T data; list_node<T> *prev, *next; }

L'itérateur renvoyé par end() désigne donc un noeud existant, mais
bidon ?


Oui.

On a donc une certaine identité entre chaque itérateur
retourné par end(), depuis une même liste ou map ...


Ben, heureusement non ?

mais j'ai pas compris comment ils font si
par hasard on fait un erase() de cette racine...


Tu parles bien de o.erase( o.end() ) ?!?


Non, dans un arbre équilibré, normalement, la racine c'est
pas o.end() mais plutôt o.begin()+ o.size()/2.

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html


Désolé, je ne suis pas bruxellois...

Marc Boyer
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens...


Avatar
drkm
Marc Boyer writes:

drkm wrote:

Marc Boyer writes:

En effet, dans ces deux conteneurs, une impleméntation triviale d'un
itérateur est de faire un pointeur sur un élément interne (cellule
pour list et noeud pour map), avec une surcharge de * et ->.


Lorsque tu dis « faire un pointeur ... », j'imagine que tu veux dire
« définir une structure contenant un pointeur ... ». Il faut
également surcharger les opérateurs de comparaison, d'incrémentation
et de décrémentation.


Tout a fait.

Mais, comment coder end (et rend), en restant homogène ?
Qu'entends-tu par « homogène » ?



Pas un horible cast de la mort pour mettre un pointeur
sur conteneur dans une variable destinée à recevoir un pointeur
sur noeud interne.


Je ne comprend toujours pas. Pourquoi veux-tu dans ce cas effectuer
un cast depuis un pointeur sur conteneur ?

Ce qui me vient directement à l'esprit serait d'initialiser le
pointeur membre de l'itérateur à une valeur sentinelle, comme 0. Je
ne sais pas s'il s'agit d'une implémentation permise, mais je ne vois
pas a priori de contre-indication, ni d'avantage à avoir une autre
valeur.


C'était aussi mon idée, facile à implémenter, faite, sauf que,
comment faire end()-1 et retrouver begin()+(size()-1) ?


std::list<>::end() - 1 ?!?

1) Ajouter des éléments end/rend
Ajouter dans la liste et dans l'arbre des éléments "en plus",
des cellules/noeuds marqueurs de fin. Mais autant c'est faisable
pour la liste (avec des tests genre it->next->next != NULL
au lieu de it->next != NULL), autant dans l'arbre, ça
complique tous les codes.


Je ne vois pas l'intérêt.


De end(), on passe facilement à end()-1


Mais je ne vois pas l'intérêt dans le cas de std::list<> et
std::map<>.

J'ai tenté de jetter un coup d'oeuil sur les sources
de la stl de gcc, et les itérateurs semblent n'etre qu'un
pointeur.


Huh ? Autant je comprendrais avec std::vector (et il me semble
qu'il s'agissait effectivement d'une implémentation courante), autant
je n'arrive pas à voir comment utiliser un simple pointeur comme
itérateur de std::list ou std::map.


Pointeur sur un noeud interne. Globalement, une list<T>, c'est
un list_node<T>* avec
class list_node<T> { T data; list_node<T> *prev, *next; }


Je ne vois toujours pas comment il est possible d'utiliser un simple
pointeur, de quelque type que ce soit, comme itérateur dans le cas
d'une std::list<>.

L'itérateur renvoyé par end() désigne donc un noeud existant, mais
bidon ?


Oui.

On a donc une certaine identité entre chaque itérateur
retourné par end(), depuis une même liste ou map ...


Ben, heureusement non ?


Il me semblait que la seule contrainte de cet itérateur est d'être
comparé à un itérateur de ce conteneur. Un itérateur de fin qui
stockerait par exemple 0 comme valeur sentinelle me semblerait
acceptable (il suffit que le suivant du dernier noeud de la liste
vale lui-même 0). Mais, sans doute, je ne pense pas à certaines
contraintes ou garanties sur les itérateurs.

mais j'ai pas compris comment ils font si
par hasard on fait un erase() de cette racine...


Tu parles bien de o.erase( o.end() ) ?!?


Non, dans un arbre équilibré, normalement, la racine c'est
pas o.end() mais plutôt o.begin()+ o.size()/2.


Lorsque tu disais que std::map<>::end() pointait vers la racine de
l'arbre, je pensais que tu parlais d'une racine bidon, comme on en
trouve dans certaines implémentations d'arbres où l'on est certain
d'avoir toujours un noeud, mais ce noeud ne fait pas partie de l'arbre
lui-même. Il me semble qu'un implémentation où end() retourne un
itérateur vers un élément valide ne serait pas conforme. Mais
peut-être que je me trompe.

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html


Désolé, je ne suis pas bruxellois...


C'est pas grave, une fois ...

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html



Avatar
Marc Boyer
In article , drkm wrote:
Pas un horible cast de la mort pour mettre un pointeur
sur conteneur dans une variable destinée à recevoir un pointeur
sur noeud interne.


Je ne comprend toujours pas. Pourquoi veux-tu dans ce cas effectuer
un cast depuis un pointeur sur conteneur ?


A partir de end(), il faut pouvoir retrouver le conteneur, donc,
pointer sur quelque chose. Hors, on ne peut pas pointer sur un element
puisque les iterateurs ne doivent pas etre invalidé par la
destruction d'un élément désigné par un autre itérateur.
Sur quoi pointer donc ?

Ce qui me vient directement à l'esprit serait d'initialiser le
pointeur membre de l'itérateur à une valeur sentinelle, comme 0. Je
ne sais pas s'il s'agit d'une implémentation permise, mais je ne vois
pas a priori de contre-indication, ni d'avantage à avoir une autre
valeur.


C'était aussi mon idée, facile à implémenter, faite, sauf que,
comment faire end()-1 et retrouver begin()+(size()-1) ?


std::list<>::end() - 1 ?!?


Me semble bien qu'on peut décrémenter l'intérateur end, non ?

#include <list>

int main(){
std::list<int> li;
std::list<int>::iterator it;
li.push_back(0);
it= li.end();
it--;
assert( it == li.begin() );
return 0;
}

Ou ça marche juste parce que mon compilateur est gentil avec moi ?

1) Ajouter des éléments end/rend
Ajouter dans la liste et dans l'arbre des éléments "en plus",
des cellules/noeuds marqueurs de fin. Mais autant c'est faisable
pour la liste (avec des tests genre it->next->next != NULL
au lieu de it->next != NULL), autant dans l'arbre, ça
complique tous les codes.


Je ne vois pas l'intérêt.


De end(), on passe facilement à end()-1


Mais je ne vois pas l'intérêt dans le cas de std::list<> et
std::map<>.


Ben, c'est *une* solution.

Pointeur sur un noeud interne. Globalement, une list<T>, c'est
un list_node<T>* avec
class list_node<T> { T data; list_node<T> *prev, *next; }


Je ne vois toujours pas comment il est possible d'utiliser un simple
pointeur, de quelque type que ce soit, comme itérateur dans le cas
d'une std::list<>.


Quand je dis "simple pointeur", c'est que l'unique variable
membre est un poiteur, auquel on rajoute toutes les fonctions qui
vont autour (*, ->, l'arithmétique...)

Il me semblait que la seule contrainte de cet itérateur est d'être
comparé à un itérateur de ce conteneur. Un itérateur de fin qui
stockerait par exemple 0 comme valeur sentinelle me semblerait
acceptable (il suffit que le suivant du dernier noeud de la liste
vale lui-même 0). Mais, sans doute, je ne pense pas à certaines
contraintes ou garanties sur les itérateurs.


Ben, faire
it= li.end();
it--;
Mais est-ce valide ? Je croyais que oui mais je me plante
peut-être.

Marc Boyer
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens...




Avatar
Jean-Marc Bourguet
Marc Boyer writes:

J'ai tenté de jetter un coup d'oeuil sur les sources
de la stl de gcc, et les itérateurs semblent n'etre qu'un
pointeur. Le list semble codé avec un element en rab, en
tête (?), end() étant l'adresse de ce pointeur, et
begin étant end()->next (en gros). Pour l'arbre, ça semble
être la racine, mais j'ai pas compris comment ils font si
par hasard on fait un erase() de cette racine...


Ce ne serait pas un element en plus egalement qui serait donc garanti
de rester toujours la racine?

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
drkm
Marc Boyer writes:

In article , drkm wrote:

Pas un horible cast de la mort pour mettre un pointeur
sur conteneur dans une variable destinée à recevoir un pointeur
sur noeud interne.


Je ne comprend toujours pas. Pourquoi veux-tu dans ce cas effectuer
un cast depuis un pointeur sur conteneur ?


A partir de end(), il faut pouvoir retrouver le conteneur,


Pourquoi ?

donc,
pointer sur quelque chose. Hors, on ne peut pas pointer sur un element
puisque les iterateurs ne doivent pas etre invalidé par la
destruction d'un élément désigné par un autre itérateur.
Sur quoi pointer donc ?

Ce qui me vient directement à l'esprit serait d'initialiser le
pointeur membre de l'itérateur à une valeur sentinelle, comme 0. Je
ne sais pas s'il s'agit d'une implémentation permise, mais je ne vois
pas a priori de contre-indication, ni d'avantage à avoir une autre
valeur.


C'était aussi mon idée, facile à implémenter, faite, sauf que,
comment faire end()-1 et retrouver begin()+(size()-1) ?


std::list<>::end() - 1 ?!?


Me semble bien qu'on peut décrémenter l'intérateur end, non ?


Ce qui n'est pas la même chose.

Mais il est vrai que je pensais que la décrémentation n'était pas
non plus permise. Il me semblait avoir lu que les itérateurs de
std::list<> devaient être « at least forward iterators ». Je n'arrive
cependant pas à remettre la main dessus.

J'ai par contre en effet trouvé des « -- a.end() » dans 23.1.1/12.
Je n'arrive cependant pas à déterminer s'il s'agit d'une illustration
conceptuelle ou une réelle contrainte. Par illustration conceptuelle,
j'entend « même si cela n'est pas garanti, on fait ici appel à votre
bon sens, à titre indicatif ». Mais cela m'étonnerait.

#include <list>

int main(){
std::list<int> li;
std::list<int>::iterator it;
li.push_back(0);
it= li.end();
it--;
assert( it == li.begin() );
return 0;
}

Ou ça marche juste parce que mon compilateur est gentil avec moi ?


Parce qu'il est gentil avec toi. Il t'a déclaré assert() ;-)

Pour la décrémentation de end(), j'aurais dit que ce n'était pas
correct. Mais après avoir jeté un coup d'oeil rapide à la norme, je
ne suis plus sûr. J'essaierai de chercher plus profondément, si
personne ne poste de référence.

1) Ajouter des éléments end/rend
Ajouter dans la liste et dans l'arbre des éléments "en plus",
des cellules/noeuds marqueurs de fin. Mais autant c'est faisable
pour la liste (avec des tests genre it->next->next != NULL
au lieu de it->next != NULL), autant dans l'arbre, ça
complique tous les codes.


Je ne vois pas l'intérêt.


De end(), on passe facilement à end()-1


Mais je ne vois pas l'intérêt dans le cas de std::list<> et
std::map<>.


Ben, c'est *une* solution.


J'ai jeté également un coup d'oeil rapide à la libstdc++ de GNU. Il
semble qu'il s'agisse d'une implémentation classique d'une liste
doublement chaînée avec noeud de tête bidon.

Dans une liste vide, ce noeud bidon a ses deux pointeurs pointant
vers lui-même. Sinon, son suivant est le premier élément, et son
précédent le dernier (et il est le suivant du dernier et le précédent
du premier).

Je pense que l'utilisation d'un tel noeud permet justement de devoir
considérer moins de cas limites (moins de tests pour voir si on est à
la fin ou au début).

Et cela règle de manière élégante la décrémentation de end(), si
elle est permise.

Pointeur sur un noeud interne. Globalement, une list<T>, c'est
un list_node<T>* avec
class list_node<T> { T data; list_node<T> *prev, *next; }


Je ne vois toujours pas comment il est possible d'utiliser un simple
pointeur, de quelque type que ce soit, comme itérateur dans le cas
d'une std::list<>.


Quand je dis "simple pointeur", c'est que l'unique variable
membre est un poiteur, auquel on rajoute toutes les fonctions qui
vont autour (*, ->, l'arithmétique...)


Ok. Je pensais que tu parlais de quelque chose comme :

template < T , A = allocator< T > >
class list
{
public:
typedef T * iterator ;
typedef T const * const_iterator ;
// ...
} ;

Il me semble que c'est ce que réalisaient plusieurs implémentations
pour std::vector<>. Je pense d'ailleurs que c'est une implémentation
conforme (à moins que, quelques effets subtils au niveau du système de
typage et des templates ?).

Il me semblait que la seule contrainte de cet itérateur est d'être
comparé à un itérateur de ce conteneur. Un itérateur de fin qui
stockerait par exemple 0 comme valeur sentinelle me semblerait
acceptable (il suffit que le suivant du dernier noeud de la liste
vale lui-même 0). Mais, sans doute, je ne pense pas à certaines
contraintes ou garanties sur les itérateurs.


Ben, faire
it= li.end();
it--;
Mais est-ce valide ? Je croyais que oui mais je me plante
peut-être.


Je croyais que non mais je me plante peut-être.

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html





Avatar
drkm
drkm writes:

Il me semblait avoir lu que les itérateurs de
std::list<> devaient être « at least forward iterators ». Je n'arrive
cependant pas à remettre la main dessus.


J'ai confondu avec 23.1.1/5 :

iterator and const_iterator types for sequences must be at least
of the forward iterator category.

23.2.2/1 précise :

list is a kind of sequence that supports bidirectional iterators

C'était pourtant pas compliqué ;-)

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html

Avatar
Marc Boyer
drkm wrote:
Marc Boyer writes:
std::list<>::end() - 1 ?!?


Me semble bien qu'on peut décrémenter l'intérateur end, non ?


Ce qui n'est pas la même chose.


Oui, j'avais oublié. Je me demande pourquoi il y a une différence
d'ailleurs, mais c'est pas fondamental comme question.

int main(){
std::list<int> li;
std::list<int>::iterator it;
li.push_back(0);
it= li.end();
it--;
assert( it == li.begin() );
return 0;
}

Ou ça marche juste parce que mon compilateur est gentil avec moi ?


Parce qu'il est gentil avec toi. Il t'a déclaré assert() ;-)

Pour la décrémentation de end(), j'aurais dit que ce n'était pas
correct. Mais après avoir jeté un coup d'oeil rapide à la norme, je
ne suis plus sûr. J'essaierai de chercher plus profondément, si
personne ne poste de référence.


Bon, j'ai vu que depuis tu a posté une ref.

J'ai jeté également un coup d'oeil rapide à la libstdc++ de GNU. Il
semble qu'il s'agisse d'une implémentation classique d'une liste
doublement chaînée avec noeud de tête bidon.

Dans une liste vide, ce noeud bidon a ses deux pointeurs pointant
vers lui-même. Sinon, son suivant est le premier élément, et son
précédent le dernier (et il est le suivant du dernier et le précédent
du premier).

Je pense que l'utilisation d'un tel noeud permet justement de devoir
considérer moins de cas limites (moins de tests pour voir si on est à
la fin ou au début).


Oui, c'est assez simple pour la liste. Pour l'arbre qui est derrière
map, c'est nettement moins évident.

Et cela règle de manière élégante la décrémentation de end(), si
elle est permise.


Tout a fait.

Ben, faire
it= li.end();
it--;
Mais est-ce valide ? Je croyais que oui mais je me plante
peut-être.


Je croyais que non mais je me plante peut-être.


Donc, en fait, c'est valide.

Bon, la question de l'implémentation des itérateurs et de end()
pour list est close.
Mais pour map, ma réflexion avance mais j'hésite toujours à mettre
un pointeur sur le conteneur dans l'itérateur.

Marc Boyer
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens...



Avatar
Marc Boyer
Jean-Marc Bourguet wrote:
Marc Boyer writes:
J'ai tenté de jetter un coup d'oeuil sur les sources
de la stl de gcc, et les itérateurs semblent n'etre qu'un
pointeur. Le list semble codé avec un element en rab, en
tête (?), end() étant l'adresse de ce pointeur, et
begin étant end()->next (en gros). Pour l'arbre, ça semble
être la racine, mais j'ai pas compris comment ils font si
par hasard on fait un erase() de cette racine...


Ce ne serait pas un element en plus egalement qui serait donc garanti
de rester toujours la racine?


Sauf que, pour des questions d'équilibre, un élément racine peut
perdre sa caractéristique de racine.
Alors apres, on peut faire une Meta-racine, sans element
pertinent dedant, au dessus de la racine.
Mais si on dit alors que end() est un pointeur sur cette racine,
le passage de end() à end()-1 ou de end()-1 à end() passe en
complexité log(n). C'est pas le bout du monde, mais dans le code
de la stl g++ ils écrivent avoir codé begin() en temps constant,
et je vois pas trop l'intérêt d'avoir begin en temps constant
si le passage de end()-1 a end() est en log(n).

En fait, avec dans l'itérateur un pointeur sur l'élément *et*
un pointeur sur le conteneur, on peut avoir le passage de end()
a son prédécesseur (et inversement) en temps constant. Mais
on double la taille d'un itérateur.
Est-ce grave docteur ? ;-)

Marc Boyer
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens...


Avatar
Jean-Marc Bourguet
Marc Boyer writes:

En fait, avec dans l'itérateur un pointeur sur l'élément *et*
un pointeur sur le conteneur, on peut avoir le passage de end()
a son prédécesseur (et inversement) en temps constant. Mais
on double la taille d'un itérateur.
Est-ce grave docteur ? ;-)


Je suis partisan des iterateurs connaissant leur conteneur, meme quand
c'est possible de faire autrement. (Mais je suis aussi partisan des
iterateurs sachant quand ils sont invalides plutot que faisant
n'importe quoi...)

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

1 2