OVH Cloud OVH Cloud

iterator = NULL

58 réponses
Avatar
Henri de Solages
Bonjour.

Est-il légal d'assigner la valeur NULL à un itérateur ?

--
Get rid of the final underscore of my address to use it.

10 réponses

1 2 3 4 5
Avatar
Stan
"Henri de Solages" a écrit dans le message de news:
dfp0sk$4a1$
Si tu as un tableau, pourquoi voudrais-tu le terminer par par NULL ?
Connaître la taille du tableau ne suffit pas pour savoir quand
s'arrêter ?


C'est bien ce que je pensais faire pour le cas avéré où l'on n'a pas le
droit d'utiliser NULL,
mais c'est plus lourd : quand je passe le tableau à une fonction, il faut
aussi lui passer sa taille.



Pas si c'est un conteneur : vector::size( ), etc.

--
-Stan


Avatar
Loïc Joly

Pourquoi ne pas placer ces itérateurs dans un vector ?



Je n'aime pas les vector car, quand on fait un erase, les iterateurs sur
les élements suivants ne sont plus valides. J'aime bien qu'on fasse
suivre le courrier quand on déménage.


En ce cas là, pourquoi ne pas placer ces itérateurs dans une list ?

--
Loïc


Avatar
Gabriel Dos Reis
Henri de Solages writes:

| > Si tu as un tableau, pourquoi voudrais-tu le terminer par par NULL ?
| > Connaître la taille du tableau ne suffit pas pour savoir quand
| > s'arrêter ?
|
| C'est bien ce que je pensais faire pour le cas avéré où l'on n'a pas le droit d'utiliser NULL,
| mais c'est plus lourd : quand je passe le tableau à une fonction, il
| faut aussi lui passer sa taille.

Non. Tu passes le vecteur (par reference ou const reference) et il
connait sa taille.

-- Gaby
Avatar
Gabriel Dos Reis
Henri de Solages writes:

| >> Je remarque cependant que ni c++ ni icpc (compilateur d'Intel)
| >> ne me jettent quand j'affecte NULL à un itérateur.
| > Amusant: tu as un bout de code.
|
| # include <iostream>
| # include <string>
| # include <list>
|
| using namespace std ;
|
| int main( int argc, char ** argv ) {
| list< string >::const_iterator i_nombres = NULL ;
|
| if( i_nombres != NULL )
| cerr << "i_nombresn" ;
| else
| cerr << "non i_nombresn" ;
| return 0 ;
| }

à l'évidence, c'est un bug dans GCC/libstdc++. Si je modifie ton program
comme suit (en ajoutant un vector)

# include <iostream>
# include <string>
# include <list>
#include <vector>

using namespace std ;

int main( int argc, char ** argv ) {
list< string >::const_iterator i_nombres = NULL ;
vector<string>::const_iterator p = NULL;

if( i_nombres != NULL )
cerr << "i_nombresn" ;
else
cerr << "non i_nombresn" ;
return 0 ;
}

J'ai un message d'erreur

hs.C: In function 'int main(int, char**)':
hs.C:10: error: conversion from 'int' to non-scalar type '__gnu_cxx::__normal_iterator<const std::string*, std::vector<std::string, std::allocator<std::string> > >' requested


-- Gaby
Avatar
Gabriel Dos Reis
Marc Boyer writes:

| Henri de Solages a écrit :
| >>> Je remarque cependant que ni c++ ni icpc (compilateur d'Intel) ne me jettent quand
| >>>j'affecte NULL à un itérateur.
| >>
| >> Amusant: tu as un bout de code.
| >
| > # include <iostream>
| > # include <string>
| > # include <list>
| >
| > using namespace std ;
| >
| > int main( int argc, char ** argv ) {
| > list< string >::const_iterator i_nombres = NULL ;
| >
| > if( i_nombres != NULL )
| > cerr << "i_nombresn" ;
| > else
| > cerr << "non i_nombresn" ;
| > return 0 ;
| > }
|
| En effet, compile sans rien dire avec mon g++ (gcc 3.3.5).
| Je suis étonné.

Yup. Essaie

#inclue <vector>

int main()
{
std::vector<int>::const_iterator p = NULL;
}

[...]

| - pour les experts, est-ce qu'il ne faut pas considérer
| l'absence d'explicit comme un bug à rapporter à
| l'équipe de GCC ?

C'en est un. Peux-tu faire un bug report ? (mets moi en CC: du PR, mon
email chez GNU est gdr at gcc dot gnu dot org).

-- Gaby
Avatar
Gabriel Dos Reis
Henri de Solages writes:

| >>Je veux un tableau d'itérateurs sur des éléments d'une list.
|
| > Sinon, ce dont tu as besoin, c'est d'un délimiteur de
| > fin, donc le end() de la liste me semble une bonne
| > valeur interdite.
|
| Mais si j'ajoute ensuite des éléments à ma liste, le end() de la list
| est-il tenu par la norme de garder une même valeur ?

Tu n'as pas à te soucier de end() -- il suffit que tu ne le stockes
nulle part (c'est une proprieté intrinsèque de chaque liste à chaque
instant). Stockes uniquement les itérateurs vers les éléments que tu
as effectivement mis dans la liste, non des artéfacts comme begin()/end().

-- Gaby
Avatar
Gabriel Dos Reis
Henri de Solages writes:

|
| > Pourquoi ne pas placer ces itérateurs dans un vector ?
|
| Je n'aime pas les vector car, quand on fait un erase, les iterateurs
| sur les élements suivants ne sont plus valides.

Oui mais d'après ce que tu as dit jusqu'à présent, tu ne gardes pas
des itérateurs (de vecteurs) vers les itérateurs de (listes), donc ta
crainte n'a aucun fondement dans ce cas ici. De plus, si tu ne fais
pas de erase() sur ton tableau, pourquoi veux-tu en faire sur un
vector ?

-- Gaby
Avatar
Gabriel Dos Reis
"kanze" writes:

| ci-dessus. J'ai l'impression que la seule différence entre leur
| code et ce que je faisais auparavent (depuis au moins vingt ans
| maintenant), c'est qu'ils allouent la racine dynamiquement,
| tandis que chez moi, elle était membre.

J'ai du mal à voir l'allocation dynamique dans

template<typename _Tp, typename _Alloc>
class _List_base
{
protected:
[...]
struct _List_impl
: public _Node_Alloc_type
{
_List_node_base _M_node;
_List_impl (const _Node_Alloc_type& __a)
: _Node_Alloc_type(__a)
{ }
};

_List_impl _M_impl;

[...]

template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class list : protected _List_base<_Tp, _Alloc>
{
[...]


-- Gaby
Avatar
Gabriel Dos Reis
Marc Boyer writes:


[...]

| > Je crois. Je crois qu'il font à peu près comme j'ai fait
| > ci-dessus. J'ai l'impression que la seule différence entre leur
| > code et ce que je faisais auparavent (depuis au moins vingt ans
| > maintenant), c'est qu'ils allouent la racine dynamiquement,
|
| Oui.

Non, il n'y a pas d'allocation dynamique de la racine.

| La classe possède un champ _M_node, qui semble être

struct _List_impl
: public _Node_Alloc_type
{
_List_node_base _M_node;
_List_impl (const _Node_Alloc_type& __a)
: _Node_Alloc_type(__a)
{ }
};

Cette racine n'est pas allouée dynamiquement.

| cette racine (d'ailleurs, end() se contente de retourner
| _M_node et begin() retourne _M_node->next), et le constructeur
| fait
| _M_node= _M_get_node();

Le constructeur fait ceci :

_List_base(const allocator_type& __a)
: _M_impl(__a)
{ _M_init(); }

Et _M_init() est:

void
_M_init()
{
this->_M_impl._M_node._M_next = &this->_M_impl._M_node;
this->_M_impl._M_node._M_prev = &this->_M_impl._M_node;
}

Je ne vois toujours pas d'allocation dynamique.

-- Gaby
Avatar
Gabriel Dos Reis
korchkidu writes:

| Gabriel Dos Reis wrote:
| > Loïc Joly writes:
| > | > Bonjour.
| > | > Est-il légal d'assigner la valeur NULL à un itérateur ?
| > | | Souvent, l'équivalent moral de NULL pour un itérateur sur une
| > | collection "c" est c.end()
| > Cela peut cependant réserver des surprises.
| > list<int> l;
| > // ...
| > copy(l.begin(), l.end(), back_inserter(l));
|
| JE vois pas le piege la. Tu peux expliquer stp?

OK.

#include <list>
#include <algorithm>
#include <iterator>

int main()
{
using namespace std;
list<int> l;
l.push_back(9);
copy(l.begin(), l.end(), back_inserter(l));
}

boucle indéfiniment, parce que même si l.end() est évalué avant de
rentrer dans le coprs de copy(), sa valeur intrinsèque change à chaque
push_back() -- ce n'est jamais la même, en particulier, elle marque la
fin de la « nouvelle » liste qui ne peut jamais être atteinte dans la
boucle de copy().

-- Gaby
1 2 3 4 5