std::find

std::find - C++ - Programmation

Marsh Posté le 15-05-2009 à 14:11:50    

soit:
- une classe B héritant d'une classe abstraite A
- un vecteur de A* instancié comme étant des objets B
- une classe C, membre donné de B
 
 
je veux utiliser std::find sur le vecteur de A* en comparant les objets C de chaque A* avec une variable de type C.
 
 
Si je redéfinis un opérateur == virtual pur  dans A puis l'implémente dans B
bool operator ==(const C& ){....}
 
ça ne marche pas, le compilo me sort  
 
binary '==' : no operator found which takes a right-hand operand of type 'const C' (or there is no acceptable conversion)
 
 
Quelqu'un voit le problème ?
 
 


---------------
je connais tout, je ne sais rien, seule certitude, à vouloir trop on finit par tout perdre.
Reply

Marsh Posté le 15-05-2009 à 14:11:50   

Reply

Marsh Posté le 15-05-2009 à 14:31:30    

A::operator==(const C& ) permet de comparer A avec C.
 
C'est dans C que tu dois redéfinir == pour comparer deux C ensembles.

Reply

Marsh Posté le 15-05-2009 à 14:36:09    

fais un find_if et passe un prédicat que tu as écrit


---------------
last.fm
Reply

Marsh Posté le 15-05-2009 à 14:44:40    

ça serait peut-être plus facile à voir avec le code source ...


---------------
By bob.
Reply

Marsh Posté le 15-05-2009 à 14:45:57    

@Elmo: find est assez intelligent, pour savoir qu'il faut qu'il utilise les objets C des objets B pour comparer avec le 3eme argument de find, c'est à dire un objet de type C ?
 
je pensais que find comparer chaque élément contenu dans le container qu'on lui passe avec le 3eme argument, hors dans mon cas ,le 3eme arguement 'nest pas du type des deux premiers, d'où le recours à la surcharge d'un A::operator==(const C& ) dans la classe A donc, puisque le contenaire contient des A*


Message édité par frenchtoucco le 15-05-2009 à 14:49:10

---------------
je connais tout, je ne sais rien, seule certitude, à vouloir trop on finit par tout perdre.
Reply

Marsh Posté le 15-05-2009 à 14:49:56    

ton opérateur va pas être appelé automatiquement. Il va appeler des opérateurs de comparaison de A* de toute façon, là. Tu t'en sortiras très facilement avec une fonction qui prend un prédicat => passe par un find_if


---------------
last.fm
Reply

Marsh Posté le 15-05-2009 à 14:51:09    

J'avais mal saisi ton besoin on dirait.


Message édité par Elmoricq le 15-05-2009 à 14:51:28
Reply

Marsh Posté le 15-05-2009 à 14:55:27    

très bien merci pour find_if, la solution donc aurait été de rédéfinir également A::operator ==(A& ) qui appel A::operator ==(C& )


---------------
je connais tout, je ne sais rien, seule certitude, à vouloir trop on finit par tout perdre.
Reply

Marsh Posté le 15-05-2009 à 15:07:55    

non, ce n'est pas "la" solution. A partir du moment où ton conteneur manipule des pointeurs, find se contentera d'appeler un opérateur de comparaison de pointeur. En rrègle générale, tu ne voudras pas changer le comportement de cet opérateur. La solution élégante, c'est le find_if.


---------------
last.fm
Reply

Marsh Posté le 15-05-2009 à 15:16:22    

oui thx


---------------
je connais tout, je ne sais rien, seule certitude, à vouloir trop on finit par tout perdre.
Reply

Marsh Posté le 15-05-2009 à 15:16:22   

Reply

Marsh Posté le 15-05-2009 à 21:06:27    

faut avouer que c'est reloud/pas élégant de créer une classe rien que pour ça

Reply

Marsh Posté le 17-05-2009 à 00:11:42    

d'ailleurs c'est possible avec une structure de données comme ça :

Code :
  1. struct Section
  2. {
  3.  string m_section_name;
  4.  vector< std::pair<string,string> > m_keys_values;
  5. };
  6. Section sec[50]


de recherche une valeur à partir d'une key de type string, en utilsant std::find ??

Message cité 1 fois
Message édité par Glock 17Pro le 17-05-2009 à 15:04:35
Reply

Marsh Posté le 17-05-2009 à 18:04:14    

deuxième question, comment tester le retour d'un find_if qui n'a rien touvé, je me retrouve avec un iterator non intialisé , comment gérer ça ?

Reply

Marsh Posté le 17-05-2009 à 20:17:01    

Glock 17Pro a écrit :

faut avouer que c'est reloud/pas élégant de créer une classe rien que pour ça


t'es payé à la ligne de code ?
Y a rien d'inélégant dans cette solution

Reply

Marsh Posté le 18-05-2009 à 10:41:26    

Glock 17Pro a écrit :

deuxième question, comment tester le retour d'un find_if qui n'a rien touvé, je me retrouve avec un iterator non intialisé , comment gérer ça ?


 
ca retour l'itérateur de fin, quand ca ne trouve rien


---------------
last.fm
Reply

Marsh Posté le 18-05-2009 à 10:42:45    

Glock 17Pro a écrit :

d'ailleurs c'est possible avec une structure de données comme ça :

Code :
  1. struct Section
  2. {
  3.  string m_section_name;
  4.  vector< std::pair<string,string> > m_keys_values;
  5. };
  6. Section sec[50]


de recherche une valeur à partir d'une key de type string, en utilsant std::find ??


 
Non, faut passer par un find_if
 
Et garde le courage ... dans C++0x, ce sera encore plus facile à utiliser grâce aux lambdas :)


---------------
last.fm
Reply

Marsh Posté le 18-05-2009 à 12:55:01    

theshockwave a écrit :


 
Non, faut passer par un find_if
 
Et garde le courage ... dans C++0x, ce sera encore plus facile à utiliser grâce aux lambdas :)


 
un seul et unique find_if, pour aller chercher une string dans un section->vector->pair  ?

Reply

Marsh Posté le 18-05-2009 à 13:31:46    

ben, tu vas sans doute préférer boucler sur ton tableau et faire un find_if par Section, après, si tu veux juste la première section contenant la clé et que tu te tamponnes de la valeur associée, tu peux faire un find_if sur ton tableau avec un prédicat qui refait un find_if dans ta section.


---------------
last.fm
Reply

Marsh Posté le 18-05-2009 à 14:42:42    

et ça reste mieux (mieux en terme de je sais pas quoi d'ailleurs, de perf sans doute) d'utiliser ce genre d'approche plutôt que de faire une recherche avec  des pauvres boucle for ? car au niveau du nombre de lignes de code c'est pas la même ...

Reply

Marsh Posté le 18-05-2009 à 15:37:04    

ben, à partir du moment où tu te poses la question des perfs de tes recherches, c'est peut-être que ton vector< string, string > n'était pas un bon choix, ou alors que tu devrais considérer l'idée de trier ton vector avant de chercher dedans, auquel cas, oui, il vaudrait mieux te faire une recherche dichotomique dedans que passer par un find_if.
 
find_if est juste là pour te simplifier la vie. Il faut voir si ton prédicat va être réutilisable, par exemple. Accessoirement, aussi, tu n'es pas obligé d'écrire une classe toi-même.
Et puis pour la lisibilité du code, ca me semble quand même être plus chouette. Un petit exemple :
 

Code :
  1. CompVector::iterator pPosition = find_if( oComps.begin(), oComps.end(), bind2nd( ptr_fun( CompInsertionPredicate ), oComp.GetPriority() ) );
  2. m_oComps.insert( pPosition, xPtr );


 
Ca me semble largement plus joli que de faire la recherche manuellement avant l'insertion
 
Edit: modif des noms de variables


Message édité par theshockwave le 18-05-2009 à 15:38:27

---------------
last.fm
Reply

Marsh Posté le 18-05-2009 à 16:34:59    

find_if ne fait que faire des boucles while de toutes façon.  
Les perfs d'une STL proprement codé (aka non M$) est identique au pouillement prés au code générique équivalent.

Reply

Marsh Posté le 21-05-2009 à 23:29:09    

En faite si on veut utiliser les algo de la stl pour recherche dans une structure de donnés comme ci-dessous, y a pas plus simple que de
faire comme ça ?
C'est censé être plus rapide que for + for   ?  

Code :
  1. /****/
  2. struct A
  3. {
  4. vector<pair<string,string> > v;
  5. };
  6. /****/
  7. /****/
  8. template <typename T> struct ValeurNulle;
  9. template <> struct ValeurNulle<int>         {static int Zero()         {return 0;}};
  10. template <> struct ValeurNulle<std::string> {static std::string Zero() {return "Not Found";}};
  11. /****/
  12. template<typename T=string>
  13. class Finder
  14. {
  15.  struct Finder2
  16.  {
  17.   T val;
  18.   Finder2(T pval):val(pval){}
  19.   bool operator()( pair<T,T>& x ){return x.first == val;}
  20.  };
  21. public:
  22.  T res,val;
  23.  Finder(T pval):val(pval){res=ValeurNulle<T>::Zero();}
  24.  void operator()( A* x )
  25.  {  
  26.   vector< pair<string,string>  >::iterator it;
  27.   it=find_if( x->v.begin(), x->v.end(), Finder2(val) );
  28.   if( it != x->v.end() )
  29.    res=(*it).second;
  30.  }
  31. };
  32. /****/
  33. void initVector(vector<A*>& v)
  34. {   
  35. A* a1,*a2,*a3;
  36. a1=new A;a2=new A;a3=new A;
  37. a1->v.push_back(pair<string,string>("totfo","totoE" ));
  38. //..
  39. v.push_back(a1);
  40. //...
  41. }
  42. /****/
  43. void main()
  44. {
  45. vector<A*> v;
  46. initVector(v);
  47.         Finder<> result = for_each(v.begin(), v.end(), Finder<>("toto" ));
  48.    cout << result.res<<endl;
  49. }


Message édité par Glock 17Pro le 21-05-2009 à 23:49:07
Reply

Marsh Posté le 22-05-2009 à 01:14:58    

Euh, ta proposition est plutôt complexe, oui. Ta structure ValeurNulle n'a d'utilité que dans le cas où tu ne vas pas préciser l'élément recherché ... Est-ce vraiment nécessaire de permettre ca, du coup ?
 
D'autre part, vu que tu fais un for_each, tu es conscient que tu ne vas pas t'arrêter quand tu auras trouvé le bon élément, n'est-ce pas ?
 
si tu veux faire ca, je pense que tu as meilleur temps d'itérer à la main sur ton vector<A*>
 
du coup, ca te donne un main du type :

Code :
  1. int main()
  2. {
  3.     vector<A*> v;
  4.     initVector(v);
  5.     String result =
  6.     for( vector<A*>::iterator itA = v.begin; itA != v.end(); ++itA )
  7.     {
  8.         vector< string, string >::iterator result = std::find_if( itA->v.begin(), itA->v.end(), bind2nd( equal_to<string>(), "toto" ) );
  9.         if( result != itA->v.end() )
  10.         {
  11.             cout << "found : " << result->second << endl;
  12.             return 0;
  13.         }
  14.     }
  15.     cout << "not found !"<< endl;
  16.     return 1;
  17. }


 
Et du coup, tu n'as plus besoin de ta structure de Finder non plus
 
Edit :
Si tu veux te poser des questions de perfs, tu devrais peut-être envisager d'avoir un conteneur associatif type map plutôt qu'un vecteur avec un système de clé/valeur géré à la main. Et si tu tiens à garder un vecteur, tu peux au moins t'assurer qu'il est trié auquel cas, tu préfèreras faire une recherche dichotomique par toi-même plutôt que reposer sur un find_if, évidemment ... Mais entre un find_if et une itération "bête" à la main, tu n'auras pas de différence de perf.


Message édité par theshockwave le 22-05-2009 à 01:23:51

---------------
last.fm
Reply

Marsh Posté le 22-05-2009 à 09:05:35    

et là avec ce code, il va tester le premier élément contenu dans pair, ça marche juste pour un vector de string, no?  
 
PS: oui dans mon code j'ai oublié de faire un if(test!=val) avant chaque appel à find_if, mais bon même le for_each est pas adapté là oui...


Message édité par Glock 17Pro le 22-05-2009 à 09:26:53
Reply

Marsh Posté le 22-05-2009 à 13:05:09    

argh, il était tard et j'ai fait quelques erreurs
 
à la place du equal_to, il faudrait faire un truc du style :
 

Code :
  1. bool StringKeyPredicate( const pair< string, string >& element, const string& val )
  2. {
  3.   return element.first == val;
  4. }
  5. // ... main ...
  6. vector< pair< string, string > >::iterator result = std::find_if( itA->v.begin(), itA->v.end(), bind2nd( ptr_fun( StringKeyPredicate ), "toto" ) );
  7. // ...


 
modulo les autres étourderies que j'ai pu faire
 
 
Edit :
Là, oui, c'est juste adapté pour le vector< string, string >, mais bon, est-ce vraiment un problème ? Tu peux adapter ca facilement pour les autres cas, et ca ne me semble pas avoir trop de sens de faire ca en générique (enfin, à la limite, tu mettre le prédicat en template, éventuellement) et à titre de rappel, en C++0x, la recherche sera encore plus simple :

Code :
  1. vector< pair< string, string > >::iterator result = std::find_if( itA->v.begin(), itA->v.end(), []( const pair< string, string > &val ){ return val.first == "toto";} ) );


Donc attention à ne pas faire une uzine à gaz qui sera remplacée prochainement par un truc simple :)


Message édité par theshockwave le 22-05-2009 à 13:10:25

---------------
last.fm
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed