OVH Cloud OVH Cloud

Conteneur et itérations

4 réponses
Avatar
Frédéric Mayot
Bonjour,

Deux questions très simples (enfin je crois...)


1) J'ai de nombreux conteneurs de pointeurs et je fais fréquemment des
itérations pour appeler une méthode. J'aurais souhaité utiliser for_each
mais étant donné que j'appelle des fonctions membres, je ne vais pas
faire un foncteur pour chacune d'entre elle. Comment puis-je faire ?


2) J'itère sur un conteneur (list) de pointeurs et j'appelle une méthode
membre pour chaque élément du conteneur. Le souci est que l'élément
contenu à la possibilité de s'extraire du conteneur, c'est à dire que
erase(iterator) n'est pas appelé au sein de la boucle. Par conséquent,
comme je ne souhaite pas que ma méthode retourne un itérateur, je suis
obligé de récupérer le pointeur suivant à chaque itération. Un peu de
code sera plus clair :

iterator current = conteneur.begin();
if (current != conteneur.end())
{
iterator next = current;
++next;

for (;current != conteneur.end(); current = next++)
(*current)->ma_methode();
}

(J'ai consciemment laissé le next++ qui fait pourtant une incrémentation
sur un itérateur de fin. Quel est le comportement ? Est-ce dangereux de
laisser cela ?)

Ce code n'est pas très joli mais je ne sais pas trop si je peux en faire
quelque chose de mieux. Pour être plus précis, ma_méthode() appelle une
méthode du conteneur qui extrait l'objet car ceci est cohérent avec mon
modèle de simulation.

Merci.

Fred

4 réponses

Avatar
Benoit Rousseau
Frédéric Mayot wrote:
Bonjour,

Deux questions très simples (enfin je crois...)


1) J'ai de nombreux conteneurs de pointeurs et je fais fréquemment des
itérations pour appeler une méthode. J'aurais souhaité utiliser for_each
mais étant donné que j'appelle des fonctions membres, je ne vais pas
faire un foncteur pour chacune d'entre elle. Comment puis-je faire ?
Un foncteur avec un pointeur sur les fonctions de la classe si elles ont

le même proto

2) J'itère sur un conteneur (list) de pointeurs et j'appelle une méthode
membre pour chaque élément du conteneur. Le souci est que l'élément
contenu à la possibilité de s'extraire du conteneur, c'est à dire que
erase(iterator) n'est pas appelé au sein de la boucle. Par conséquent,
comme je ne souhaite pas que ma méthode retourne un itérateur, je suis
obligé de récupérer le pointeur suivant à chaque itération. Un peu de
code sera plus clair :
J'ai eu un problème encore plus vissieux où n'importe lequel des

iterateurs pouvait être extrait d'une liste (même le "next" de ton
exemple) : voir plus bas.

iterator current = conteneur.begin();
if (current != conteneur.end())
{
iterator next = current;
++next;

for (;current != conteneur.end(); current = next++)
(*current)->ma_methode();
}
Ca semble marcher dans ton cas... Je ne crois pas que le if( !=end() )

soit nécessaire. Le test est fait dans le for
++next se replacera sur conteneur.end() si la liste est vide.

Pour ma part, je simulais un comportement de double-queue (on retire la
tête pour la placer à la fin de l'autre queue) avec des fonctions. La
suppression se faisait par une fonction qui avancait l'iterateur si
c'etait l'iterateur courant qui était effacé.
Inconvéniant : 1 seul iterateur possible pour faire une boucle où
l'élément peut être retirer (je pouvais toujours faire des boucles si
j'étais sûr de ne pas y retirer un élément).

Ca donnait à peu près ca :

iterator head;

iterator list_begin() {
return head = conteneur.begin();
}

iterator next() {
return ++head;
}

remove( Element*e ) {
if( *head == e ) {
head ++;
conteneur.erase( head-1 );
} else {
conteneur.remove( e );
}
}


(J'ai consciemment laissé le next++ qui fait pourtant une incrémentation
sur un itérateur de fin. Quel est le comportement ? Est-ce dangereux de
laisser cela ?)

Ce code n'est pas très joli mais je ne sais pas trop si je peux en faire
quelque chose de mieux. Pour être plus précis, ma_méthode() appelle une
méthode du conteneur qui extrait l'objet car ceci est cohérent avec mon
modèle de simulation.

Merci.

Fred




--
--------------------------------------------
Benoît Rousseau : roussebe at spray dot se
Jouez en programmant : http://realtimebattle.sourceforge.net/

Avatar
Samuel Krempp
le Friday 12 December 2003 17:55, écrivit :
1) J'ai de nombreux conteneurs de pointeurs et je fais fréquemment des
itérations pour appeler une méthode. J'aurais souhaité utiliser for_each
mais étant donné que j'appelle des fonctions membres, je ne vais pas
faire un foncteur pour chacune d'entre elle. Comment puis-je faire ?


tu peux générer un objet qui se comportera comme la fonction voulue via son
operator() (on appelle ces fonctions des functors..) lors de l'appel à
for_each, dans la ligne de code (qui a tendance à devenir longue du coup)

à base d'un ensemble de briques (de la stdlib, de libs supplémentaires, ou
encore de tes propres functor de base).

ou (ou et) à l'aide d'un mécanisme dédié à noter en ligne l'expression d'une
fonction : les fonctions lambda, et d'une librairie qui amène une syntaxe
C++ (et les objets correspondants) pour cela, aussi confortable que le C++
le permet (c'est à dire pas bcp) : boost::lambda
http://www.boost.org/libs/lambda/doc/index.html

un exemple d'utilisation de functors construits avec la stdlib
(v1, v2, et result sont des containers)
std::transform( v1.begin(), v1.end(), v2.begin(), result.begin(),
std::compose2(
std::divides<int>(),
std::bind2nd( std::multiplies<int>(), 200 ),
std::identity() ) );


devient avec boost::lambda :

#include <boost/lambda/lambda.hpp>
using namespace boost::lambda;

transform(v1.begin(), v1.end(), v2.begin(), result.begin(),
(_1 * 200) / _2 );

nettement mieux non ?

mais bon c'est pas tjrs aussi joli, il faut parfois se contorsionner un peu
pour générer le functor qu'on veut avec boost::lambda.
Entre autre si on veut appeler une certaine fonction membre sur chaque
élément des containers, c'est déja un peu plus verbeux avec
bind2nd(mem_fun_ref( <pointeur vers la fonction membre>), arguments... )

class A {
public :
void set_from_int(int) ;
};

...
vector<A> v;
int x;
for_each(v.begin(), v.end(), bind2nd(mem_fun_ref(&A::set_from_int), x) );


Et le pire, c'est si la méthode que tu veux appeler est surchargée, car
alors il n'y a aucun moyen pratique C++ de préciser laquelle, on donne le
prototype voulu comme ça :
void (A::* pmf)(const int) = &A::set_from_int;
for_each(v.begin(), v.end(), bind2nd(mem_fun_ref(pmf), x) );



la question était simple (en tout cas pour un matheux, générer des functors,
c'est un concept pas sorcier), mais les solutions possibles un peu
moins ..
j'ai essayé de résumer la situations du mieux que j'ai pu :)

--
Sam

Avatar
Loïc Joly
Frédéric Mayot wrote:

Bonjour,

Deux questions très simples (enfin je crois...)


1) J'ai de nombreux conteneurs de pointeurs et je fais fréquemment des
itérations pour appeler une méthode. J'aurais souhaité utiliser for_each
mais étant donné que j'appelle des fonctions membres, je ne vais pas
faire un foncteur pour chacune d'entre elle. Comment puis-je faire ?



Créer un foncteur, soit avec les fonctions de la bibliothèque standard,
soit autrement. Pour ça, j'aime bien boost::bind, qui m'a l'air moins
"louche" que boost::lambda dont parle Samuel Krempp dans sa réponse, et
reste agréable à utiliser. Ca donne :

class A
{
void f();
};


vector<A> vec;

std:for_each(vec.begin(), vec.end(), boost::bind(&A::f, _1));

--
Loïc

Avatar
Gabriel Dos Reis
Samuel Krempp writes:

| transform(v1.begin(), v1.end(), v2.begin(), result.begin(),
| (_1 * 200) / _2 );
|
| nettement mieux non ?

ça dépend si on est fan des _[1-9] ou non ;-p

-- Gaby