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

C++ Discussion :

Return à l'intérieur d'une boucle for -> warning


Sujet :

C++

  1. #1
    Membre confirmé
    Inscrit en
    Juin 2008
    Messages
    140
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 140
    Par défaut Return à l'intérieur d'une boucle for -> warning
    Bonjour,

    j'ai une classe Element de la sorte :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Element
    {
        public:
            std::string m_id;
            int m_value
    };
    j'ai un vecteur d'éléments de type Element :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::vector< Element * > * theVectorOfElements;
    j'ai une fonction de recherche d'un élément Element dans le vecteur en fonction de son identifiant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Element * searchElement( std::string anId , std::vector< Element * > * aVectorOfElements );
    sachant que :
    - tous les attributs m_id des différents éléments du vecteur sont distincts
    - lorsque je fais une recherche pour anId, je suis sûr qu'il existe bien un élément du vecteur qui à comme attribut m_id cet valeur anId (ce qui suppose en plus que ce vecteur n'est pas vide.

    j'ai donc implémenté cette fonction de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Element * searchElement( std::string anId , std::vector< Element * > * aVectorOfElements )
    {
        std::vector< Element  * >::iterator anIter = aVectorOfElements->begin();
        for( ; anIter != aVectorOfElements->end() ; ++anIter )
        {
            if( (*anIter)->m_id == anId )
            {
                return( (*anIter) );
            }
        }
    }
    Or j'ai un warning à la compilation :
    "warning: control reaches end of non-void function"
    dû au fait que le "return" est dans la boucle.
    Comme ce vecteur est très grand et selon mes hypothèses, je préfère ne pas avoir à le parcourir en entier si pas besoin. Comment puis-je faire pour éviter ce warning ?

    Peut-être en faisant une boucle while de la sorte (avec une variable temporaire booléenne) :
    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
    Element * searchElement( std::string anId , std::vector< Element * > * aVectorOfElements )
    {
        Element * theReturnedElement = NULL;
        bool isFind = false;
        std::vector< Element  * >::iterator anIter = aVectorOfElements->begin();
        while( ( false == isFind ) & ( anIter != aVectorOfElements->end() ) )
        {
            if( (*anIter)->m_id == anId )
            {
                isFind = true;
                theReturnedElement  = *anIter;
            }
            ++anIter;
        }
        return( theReturnedElement  );
    }
    Mais cela m'oblige à rajouter deux variables internes à la fonction.

  2. #2
    Membre Expert
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Par défaut
    Salut,

    avec un break

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
      for ( ... ) {
        if (true)
         break;
      }
     
      return ... ;

  3. #3
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Citation Envoyé par minnesota Voir le message
    Salut,

    avec un break

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
      for ( ... ) {
        if (true)
         break;
      }
     
      return ... ;
    Son erreur ne vient pas du fait qu'il ai un return dans sa boucle for mais qu'il n'ai pas de return après sa boucle for. En effet, ta fonction doit forcément retourner quelque chose vu qu'elle n'est pas de type 'void', or que se passe-t-il si ta condition du 'if' n'est jamais vérifiée et que tu sors de ton for ? Tu ne retourne alors rien.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Element * searchElement( std::string anId , std::vector< Element * > * aVectorOfElements )
    {
        std::vector< Element  * >::iterator anIter = aVectorOfElements->begin();
    
        for( ; anIter != aVectorOfElements->end() ; ++anIter )
            if( (*anIter)->m_id == anId )
                return *anIter;
    
         return NULL;
    } //tu pouvais aussi supprimer les accolades inutiles
    Faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
      for ( ; anIter != aVectorOfElements->end() ; ++anIter ) {
        if ( (*anIter)->m_id == anId )
         break;
      }
     
      return (*anIter);
    Est encore pire. Si tu sors de ta boucle, tu va effectuer l'opération *anIter qui correspondra à *aVectorOfElements->end() = opération illégale




    EDIT : je n'avais pas vu :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     Element * theReturnedElement = NULL;
    Mais tu te complique la vie bien inutilement, ton code devient un peu plus lent et tu perd aussi niveau lisibilité.




    EDIT2 : J'oubliais aussi une autre erreur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ( false == isFind ) & ( anIter != aVectorOfElements->end() )
    C'est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ( false == isFind ) && ( anIter != aVectorOfElements->end() )
    Et tu pouvais même utiliser un for :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    for(initialisation; condition1 && condition2 ; ... )
    Le tout est de ne pas trop mettre de condition de le for, 2 maximum je dirais pour rester lisible.

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,

    Il faut comprendre que le compilateur est un brave soldat qui fait ce qu'on lui dit, sans se poser de question...

    Aussi, si tu lui dit qu'une fonction doit renvoyer quelque chose, il va, tout simplement, s'étonner si, en fin de fonction, il n'y a pas une instruction de retour

    Tu as donc deux solutions :
    1. Soit tu es sur que, quoi qu'il arrive, tu trouvera effectivement l'élément dans ton tableau (mais peux tu seulement le certifier en toutes cirocnstances ), et, dans ce cas, tu peux tout simplement ignorer cet avertissement
    2. Soit, et c'est sans doute mieux, renvoie une valeur clairement identifiée comme une valeur invalide (NULL, ou nullptr en C++11 ) si l'élément n'a pas été trouvé dans le tableau.
    Il n'y a rien qui t'interdise d'avoir plusieurs instructions return dans ta fonction, mais, si elle doit renvoyer une valeur (non void ), la dernière instruction doit être un return

    Pour ne pas avoir à parcourir l'ensemble du tableau, tu peux donc parfaitement renvoyer la valeur dés que tu la trouves, mais, il est "de bon ton" de prévoir le cas où la valeur n'a pas été trouvée (ne serait-ce que parce que tu peux avoir un effet de bord "quelque part" qui peut avoir invalidé ta valeur de recherche )

    L'idéal est donc d'avoir un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Element * searchElement( std::string anId , std::vector< Element * > const & aVectorOfElements )
    {
        std::vector< Element  * >::iterator anIter = aVectorOfElements.begin();
        for( ; anIter != aVectorOfElements.end() ; ++anIter )
        {
            if( (*anIter)->m_id == anId )
            {
                return( (*anIter) );
            }
        }
        return NULL;
    }
    PS : plutot que de passer le tableau par pointeur comme tu l'as fait, passes le par référence constante, c'est plus facile à manipuler, et cela te permet de respecter plus facilement la const correctness
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Membre confirmé
    Inscrit en
    Juin 2008
    Messages
    140
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 140
    Par défaut
    Merci pour les réponses.

    Citation Envoyé par Neckara Voir le message
    Son erreur ne vient pas du fait qu'il ai un return dans sa boucle for mais qu'il n'ai pas de return après sa boucle for. En effet, ta fonction doit forcément retourner quelque chose vu qu'elle n'est pas de type 'void', or que se passe-t-il si ta condition du 'if' n'est jamais vérifiée et que tu sors de ton for ? Tu ne retourne alors rien.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Element * searchElement( std::string anId , std::vector< Element * > * aVectorOfElements )
    {
        std::vector< Element  * >::iterator anIter = aVectorOfElements->begin();
    
        for( ; anIter != aVectorOfElements->end() ; ++anIter )
            if( (*anIter)->m_id == anId )
                return *anIter;
    
         return NULL;
    } //tu pouvais aussi supprimer les accolades inutiles
    Faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
      for ( ; anIter != aVectorOfElements->end() ; ++anIter ) {
        if ( (*anIter)->m_id == anId )
         break;
      }
     
      return (*anIter);
    Est encore pire. Si tu sors de ta boucle, tu va effectuer l'opération *anIter qui correspondra à *aVectorOfElements->end() = opération illégale
    J'ai des conditions qui supposent que je trouverai toujours un élément vérifiant la condition du if.

    Citation Envoyé par Neckara Voir le message
    EDIT : je n'avais pas vu :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     Element * theReturnedElement = NULL;
    Mais tu te complique la vie bien inutilement
    Il s'agissait d'une proposition qui ne me plait pas.

    Citation Envoyé par Neckara Voir le message
    ton code devient un peu plus lent et tu perd aussi niveau lisibilité.
    Pourquoi ?

    Citation Envoyé par Neckara Voir le message
    EDIT2 : J'oubliais aussi une autre erreur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ( false == isFind ) & ( anIter != aVectorOfElements->end() )
    C'est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ( false == isFind ) && ( anIter != aVectorOfElements->end() )
    Il me semble justement qu'en ne mettant qu'un seul '&', on fait de la manière suivante :
    - on vérifie la condition ( false == isFind ) :
    - si c'est faux, on sort de la boucle
    - sinon on vérifie la condition ( anIter != aVectorOfElements->end() )
    alors qu'en mettant deux '&', on fait les deux vérifications en même temps (perte de temps si ( false == isFind ) est faux

    Citation Envoyé par Neckara Voir le message
    Et tu pouvais même utiliser un for :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    for(initialisation; condition1 && condition2 ; ... )
    Le tout est de ne pas trop mettre de condition de le for, 2 maximum je dirais pour rester lisible.
    Suivant comment tu indentes et sautes des lignes, plus de 2 conditions peut rester lisible.

  6. #6
    Membre confirmé
    Inscrit en
    Juin 2008
    Messages
    140
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 140
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Salut,

    Il faut comprendre que le compilateur est un brave soldat qui fait ce qu'on lui dit, sans se poser de question...

    Aussi, si tu lui dit qu'une fonction doit renvoyer quelque chose, il va, tout simplement, s'étonner si, en fin de fonction, il n'y a pas une instruction de retour

    Tu as donc deux solutions :
    1. Soit tu es sur que, quoi qu'il arrive, tu trouvera effectivement l'élément dans ton tableau (mais peux tu seulement le certifier en toutes cirocnstances ), et, dans ce cas, tu peux tout simplement ignorer cet avertissement
    2. Soit, et c'est sans doute mieux, renvoie une valeur clairement identifiée comme une valeur invalide (NULL, ou nullptr en C++11 ) si l'élément n'a pas été trouvé dans le tableau.
    Il n'y a rien qui t'interdise d'avoir plusieurs instructions return dans ta fonction, mais, si elle doit renvoyer une valeur (non void ), la dernière instruction doit être un return

    Pour ne pas avoir à parcourir l'ensemble du tableau, tu peux donc parfaitement renvoyer la valeur dés que tu la trouves, mais, il est "de bon ton" de prévoir le cas où la valeur n'a pas été trouvée (ne serait-ce que parce que tu peux avoir un effet de bord "quelque part" qui peut avoir invalidé ta valeur de recherche )

    L'idéal est donc d'avoir un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Element * searchElement( std::string anId , std::vector< Element * > const & aVectorOfElements )
    {
        std::vector< Element  * >::iterator anIter = aVectorOfElements.begin();
        for( ; anIter != aVectorOfElements.end() ; ++anIter )
        {
            if( (*anIter)->m_id == anId )
            {
                return( (*anIter) );
            }
        }
        return NULL;
    }
    PS : plutot que de passer le tableau par pointeur comme tu l'as fait, passes le par référence constante, c'est plus facile à manipuler, et cela te permet de respecter plus facilement la const correctness
    Merci beaucoup pour vos précisions.
    Concernant le passage par pointeur, il ne s'agit pas du code complet. J'ai en fait une classe ayant, entre autre, comme attribut un vecteur d'éléments de type Element ; et donc une méthode (dans cette classe) de recherche d'un élément du vecteur suivant un id :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Element searchElement( std::string anId );
    Mais je n'ai pas mis tout cela pour ne pas surcharger inutilement ma question.

  7. #7
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Il me semble justement qu'en ne mettant qu'un seul '&', on fait de la manière suivante :
    - on vérifie la condition ( false == isFind ) :
    - si c'est faux, on sort de la boucle
    - sinon on vérifie la condition ( anIter != aVectorOfElements->end() )
    alors qu'en mettant deux '&', on fait les deux vérifications en même temps (perte de temps si ( false == isFind ) est faux
    Faux, '&' est un symbole d'opérateur bit à bit :
    000 100 & 001 000 = 000 000 -> false
    000 100 && 001 000 -> true

    L'opération '&&' doit théoriquement s'arrêter à la première condition fausse (si je ne me trompe pas). Mais dans la pratique ce n'est pas toujours le cas suivant le compilateur utilisé (il faut donc se méfier).
    Par contre il me semble bien que les deux évaluation des conditions avec '&&' se fait de gauche à droite et non en même temps.

    Il faut par contre se méfier avec les fonctions car là l'ordre d'évaluation des paramètre est indéterminé (se méfier alors quand on a certaines évaluation de paramètre qui provoquent des effets de bords).

    J'ai des conditions qui supposent que je trouverai toujours un élément vérifiant la condition du if.
    C'est toujours plus joli de ne pas avoir le warning et ça ne coûte pas grand chose. De plus si jamais tes conditions changent ou si tu veux utiliser cette fonction dans le cas où l'élément n'existe pas forcément, tu n'auras rien à retouché.
    Sinon il faudrait au minimum mettre un commentaire pour expliquer cette précondition que les personnes qui lisent ton code puisse le savoir.

    Pourquoi ?
    Ton code devient plus lent car tu va de voir passer par une variable intermédiaire et quand tu trouvera le résultat tu va devoir faire un break en plus. Mais bon, c'est vraiment négligeable. Mais c'est toujours ça de gagné^^
    Niveau lisibilité, c'est plus simple de voir directement le return et la valeur retournée qu'un break et un return où il faut rechercher en haut la valeur par défaut si jamais on a pas trouvé l'élément.

    Suivant comment tu indentes et sautes des lignes, plus de 2 conditions peut rester lisible.
    Déjà si tu as plus de deux conditions, il faut déjà voir si tu n'as pas un soucis d’algorithme. Et parfois, il vaut mieux faire de simple if avec un break qui de faire une condition à rallonge.
    Bon après '2 conditions maximum' n'est pas une règle absolue, c'est plus un conseil. Le tout est de regarder au cas par cas.

  8. #8
    Membre confirmé
    Inscrit en
    Juin 2008
    Messages
    140
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 140
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Faux, '&' est un symbole d'opérateur bit à bit :
    000 100 & 001 000 = 000 000 -> false
    000 100 && 001 000 -> true

    L'opération '&&' doit théoriquement s'arrêter à la première condition fausse (si je ne me trompe pas). Mais dans la pratique ce n'est pas toujours le cas suivant le compilateur utilisé (il faut donc se méfier).
    Par contre il me semble bien que les deux évaluation des conditions avec '&&' se fait de gauche à droite et non en même temps.

    Il faut par contre se méfier avec les fonctions car là l'ordre d'évaluation des paramètre est indéterminé (se méfier alors quand on a certaines évaluation de paramètre qui provoquent des effets de bords).
    Autant pour moi alors. Justement je croyais que cette différence permettait d'éviter de potentiels effets de bord.

    Citation Envoyé par Neckara Voir le message
    C'est toujours plus joli de ne pas avoir le warning et ça ne coûte pas grand chose. De plus si jamais tes conditions changent ou si tu veux utiliser cette fonction dans le cas où l'élément n'existe pas forcément, tu n'auras rien à retouché.
    Sinon il faudrait au minimum mettre un commentaire pour expliquer cette précondition que les personnes qui lisent ton code puisse le savoir.
    Le rajout du "return( NULL );" me semble la solution à réaliser.
    Le commentaire était déjà inclus.

    Citation Envoyé par Neckara Voir le message
    Déjà si tu as plus de deux conditions, il faut déjà voir si tu n'as pas un soucis d’algorithme. Et parfois, il vaut mieux faire de simple if avec un break qui de faire une condition à rallonge.
    Bon après '2 conditions maximum' n'est pas une règle absolue, c'est plus un conseil. Le tout est de regarder au cas par cas.
    C'est bien ce que j'ai dit.

    Merci en tout cas pour vos réponses.

  9. #9
    Membre Expert
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Par défaut
    Même si j'ai tendance à lire très très rapidement et à répondre en premier message très très succinctement aux profils "pre inactifs", cf les abimes, j'aurais dû étoffer mon exemple inutile de 23h pour qu'il le soit .

    Aussi, définir une valeur de retour par défaut, l'assigner au besoin est une manière non moins élégante qu'une autre de faire les choses proprement (if> assignation> break). Pour ce qui est de l'optimisation prématurée, je dirais qu'optimiser un algorithme c'est une chose, perdre 10 cycles d'horloge sur un programme en phase de dev c'en est une autre.

    Bref, quoi qu'il en soit mon -2x2 est mérité (j'hésite entre et ) cependant, pourquoi lui vous lui collez aussi un -2x2 alors qu'il ne fait que poser des questions, bientôt les gens il vent avoir peut d'ouvrir des sujets

  10. #10
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Citation Envoyé par minnesota Voir le message
    Bref, quoi qu'il en soit mon -2x2 est mérité (j'hésite entre et ) cependant, pourquoi lui vous lui collez aussi un -2x2 alors qu'il ne fait que poser des questions, bientôt les gens il vent avoir peut d'ouvrir des sujets
    J'avoue, je suis une des personnes qui t'as mis un vote négatif, sans rancune ?

    Pour les votes négatif sur le post du PO, j'avoue ne pas comprendre moi-même.
    Certaines fois un vote négatif peut se comprendre quand le PO abuse un peu.
    Parfois on trouve des votes bizarres aussi, généralement dans ce cas là je demande la raison de ce vote, voir si il y a pas une erreur dans ce qui a été dit etc...

    Dans le post du PO on pourrait justifier le vote négatif par les erreurs qu'il a dit, mais je rejoins minnesota sur le fait que c'est décourageant pour la personne qui pose des questions.
    Peut-on vraiment sanctionner négativement une personne qui essaye de comprendre et de s'améliorer même si elle dit des bêtises?
    Il vaut mieux dire des bêtises ici et apprendre que de continuer à faire les mêmes erreurs.
    C'est une des raisons pour laquelle j'ai contrebalancé avec 3 votes positifs.


    Bon après, ceci n'a pas une importance fondamentale et ça n'empêchera pas le PO de dormir^^

  11. #11
    Membre Expert
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Par défaut
    On est hs, mais je te réponds par courtoisies vu que tu poses la question.

    Aussi, je le savais très bien, mais tu ne risques rien, car ma politique de vote dans la partie technique, depuis très longtemps déjà, c'est de mettre en valeur les postes de qualité (pour faire de jolis cadres verts), donc soit c'est plus, soit c'est rien. Si je suis l'auteur du sujet, je mets systématiquement un +1 à ceux qui ont la bonté de répondre, c'est ma manière de dire merci (du coup, on arrive plus vite aux jolis cadres verts vu que dans la partie technique les votes ne sont pas légion).

    Par contre, même si je l'ai déjà fait je me questionne sur l'intérêt de contrebalancer pour contrebalancer, parce que dans le fond ça ne sert pas l'intérêt général du forum, enfin, je trouve. J'ai remarqué aussi qu'il y en a qui mette un +1 sur un post pour annuler leur vote négatif d'un post précédent, alors qu'il est possible d'annuler un vote en appuyant sur le même bouton qui l'a validé. Mais bon, comme on est hs par rapport au sujet, le mieux c'est de soulever le problème dans la section adéquate.

    Et puis comme on est dans le hs, tant qu'à faire, j'en profite pour dire que j'ai capturé un moustique affamé gros comme une vache, l'été s'annonce chaud

  12. #12
    Membre expérimenté Avatar de Nogane
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 241
    Par défaut
    Bonsoir,
    Pour répondre a la question initial je pense que j'aurais ajouté une exception, du style:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    throw std::logic_error("Cette ligne ne devrait pas être atteinte");
    plutot qu'un "return NULL".
    Il y as trois raison a cela:
    1 - Le relecteur du code sait immédiatement a quoi s'en tenir
    2 - Comme le "return", ça fait taire le warning
    3 - On as pas besoin(ni même l'idée) de tester la valeur retournée à la sortie de la fonction.

  13. #13
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par Nogane Voir le message
    Bonsoir,
    Pour répondre a la question initial je pense que j'aurais ajouté une exception, du style:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    throw std::logic_error("Cette ligne ne devrait pas être atteinte");
    plutot qu'un "return NULL".
    Il y as trois raison a cela:
    1 - Le relecteur du code sait immédiatement a quoi s'en tenir
    2 - Comme le "return", ça fait taire le warning
    3 - On as pas besoin(ni même l'idée) de tester la valeur retournée à la sortie de la fonction.
    C'est effectivement une solution, mais elle ne doit être mise en oeuvre que dans le cas où tu sais pertinemment que la recherche doit donner un résultat...

    Encore une fois, il faut voir le contexte général car... peux tu vraiment certifier que, quoi qu'il arriive, tu trouveras l'élément recherché

    [édit] (oupps mes doigts ont glissé )

    Il ne faut pas oublier que l'exception va remonter la pile d'appel jusqu'à tomber sur un bloc try ... catch dont le catch est compatible avec l'exception lancée.

    Mais cela sous entend qu'il faut être en mesure de gérer l'exception, de préférence en apportant une solution qui permette d'éviter qu'elle ne se reproduise sous peine de voir ton application planter purement et simplement sur une "unhandled exception"

    Tu me diras sans doute que ce cas de figure n'est pas pire qu'un plantage du au fait que tu aurais essayé de déréférencer NULL, mais elle me semble, sous certaines conditions du moins, peut etre un peu... expéditive

    En effet, le fait qu'un élément n'existe pas n'est pas forcément une erreur et n'est pas forcément "exceptionnel" !

    Alors, effectivement, l'exception peut être adaptée à certains contexte, sous réserve que l'utilisateur reste conscient qu'elle peut etre lancée (et qu'il veille donc à l'attraper au plus tard au niveau de l'interface graphique, par exemple), mais, dans d'autres contextes, il sera peut etre beaucoup plus logique de simplement vérifier la validité du pointeur retourné
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  14. #14
    Membre expérimenté Avatar de Nogane
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 241
    Par défaut
    Je suis tout a fait d'accord que choisir throw ou return NULL dépend du contexte. Mais dans le cas présent, le PO est sur que l'objet recherché existe.

    - lorsque je fais une recherche pour anId, je suis sûr qu'il existe bien un élément du vecteur qui à comme attribut m_id cet valeur anId (ce qui suppose en plus que ce vecteur n'est pas vide.
    Pour moi, le pointeur ne sera pas NULL car ça fait partie des postconditions de la fonction.
    Tout ce qui va suivre par donc bien de l'idée que, a par a cause d'un bug, l'objet recherché sera toujours trouvé.
    Donc d'un coté on peu lancer une exception, en prenant le risque que ça stop(proprement) le programme si elle n'est pas gérée.
    De l'autre coté, on peu retourner NULL... mais réfléchissons au conséquences de ce choix:
    Toute les fonctions appelantes vont devoir tester si la valeur retournée et NULL et gérer ce cas. Pour cela, 4 choix possibles:
    1 - En retournant un pointeur NULL, qui devrai a son tour être tester et les test de pointeurs NULL vous se propager partout dans le code.
    2 - En lançant une exception(dans ce cas autant le faire de suite)
    3 - En retournant un code d'erreur, mais beaucoup de gens préfère(souvent a juste titre) utiliser des exceptions plutôt que des codes d’erreurs. En plus le teste de ce code d’erreur va se propager dans tout les fonctions appelantes.
    4 - En faisant un assert(false && "bla bla bla"), mais comme pour l'exception, ça aurais pu être fait dans la fonction "searchElement".


    Bon après, le plus important n'est pas de trouver la bonne manière de gérer l'erreur, mais de bien documenter la fonction pour que son futur utilisateur sache comment elle réagit en cas d’erreur.

  15. #15
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Dans ce cas, il a comme pré-condition qu'il trouvera l'élément.

    Mais il fait comment s'il aura, plus tard, besoin de la même fonction mais dont cette pré-condition n'est pas vérifiée?

    Il refait la même fonction en retournant cette fois NULL ?

    C'est à la fonction appelante de décider que faire si jamais il y a une erreur.

    Ce n'est pas très compliqué d'ajouter :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    if(ptr = searchElement(...))
    {
     //traitement de l'erreur
    }
    De plus, la post condition dit : si l'élément est sûr d'être trouvé, la fonction ne renverra pas NULL, dans ce cas là, il est peut être inutile de tester la valeur de retour.

    Si la fonction appelante peut lancer une exception, mais c'est à elle de la lancer. C'est elle qui doit décider de ce qu'elle doit lancer.


    EDIT : c'est comme si on avais la fonction factorielle :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    unsigned int factorielle(unsigned int i)
    {
             unsigned int resultat = 1;
             while(i != 1)
             {
                      resultat *= i;
                      i--;
             }
             return resultat;
    }
    On sait qu'on ne l'utilisera qu'avec des nombres strictement positif.
    On ne va pas gérer le cas où i =0 et on va lancer une exception à la fin de la fonction ??
    Et si un jour on a besoin de faire 0! ?

  16. #16
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Dans ce cas, il a comme pré-condition qu'il trouvera l'élément.

    Mais il fait comment s'il aura, plus tard, besoin de la même fonction mais dont cette pré-condition n'est pas vérifiée?
    Si une précondition n'est pas vérifiée, c'est la faute de l'appelant, le code appelé n'a pas à s'en occuper. Il peut (et c'est souhaitable) aider à détecter ce manquement de l'appelant, dans les cas où c'est possible, mais c'est une aide au débogage, pas le fonctionnement requis de la fonction.

    Il y a un exemple que j'aime bien, c'est la recherche dichotomique, qui impose en pré-condition que l'étendue recherchée soit triée. Si on impose une validation des pré-conditions, on passe d'une complexité en O(log n) à une complexité en O(n), qui serait celle d'un tri sans dichotomie. Autrement dit, dans ce cas, valider les préconditions rend la fonction totalement inutile...


    Donc, soit on veut faire une fonction de recherche générique, utilisable sans précautions spéciales, et qui retourne NULL quand l'élément n'est pas trouvé (et qui impose donc à l'appelant de tester cette valeur).

    Soit on veut faire une fonction de recherche d'un élément que l'on sait au préalable exister, que l'on document ainsi (ajout d'une précondition), et dans ce cas là, ce que fait la fonction dans le cas où ce n'est pas le cas, peu me chaut. J'ai tendance à préférer un simple assert, mais n'ai rien contre une exception, et peux même survivre à un plantage en bonne et due forme, voire même au fait de retourner une mauvaise valeur (même si je n'accepterais ce dernier cas que si une validation est vraiment coûteuse).

    Je pense qu'il faut vraiment distinguer dans la gestion des erreurs deux cas : Les erreurs de programmation (comme ici) et les "erreurs" qui sont en fait la gestion de cas particuliers (par exemple, gérer la saisie d'une valeur incorrecte par l'utilisateur).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  17. #17
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Si une précondition n'est pas vérifiée, c'est la faute de l'appelant, le code appelé n'a pas à s'en occuper. Il peut (et c'est souhaitable) aider à détecter ce manquement de l'appelant, dans les cas où c'est possible, mais c'est une aide au débogage, pas le fonctionnement requis de la fonction.
    C'est pour cela que je lui demandais s'il faut réécrire une fonction quasi identique qui cette fois retourne NULL puisqu'on ne peut pas utiliser la fonction originelle vu que la précondition n'est pas vérifiée.
    Ce qui est tout de même du gaspillage.


    On a aussi une "post-condition" : si l'élément existe dans la liste, alors la fonction ne renverra jamais NULL. Dans ce cas là si on sait que l'élément existe, est-il vraiment nécessaire de tester la valeur de retour ?

  18. #18
    Membre confirmé
    Inscrit en
    Juin 2008
    Messages
    140
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 140
    Par défaut
    Merci à tous pour vos réponses et remarques.

    Comme je l'ai dit, dans tous les cas l'id se trouve dans le tableau, sinon c'est qu'il y a eu une erreur préalable (lors du scan d'un des fichiers de configuration, ou problème d'espace mémoire de la machine ?) et le programme n'a donc pas lieu de fonctionner.
    Je retiens la solution consistant à faire un return(NULL), reste à voir s'il faut que je vérifie justement la validité du pointeur dans la fonction appelante. Vos dernières remarques me sont donc utiles.

  19. #19
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2011
    Messages
    13
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2011
    Messages : 13
    Par défaut
    La fonction ayant comme précondition que l'élément recherché doit être présent, dans ce cas a mon sens elle ne doit pas renvoyer un pointeur mais une référence.

    Du coup le type de retour explicite clairement la précondition et le contexte appelant n'a même plus a se poser la question de savoir si il doit ou non tester le cas null.

    Par extension la fonction n'ayant plus la possibilité de renvoyé un pointeur null, le cas d'erreur doit être traite autrement, par exemple via une exception.

  20. #20
    Membre confirmé
    Inscrit en
    Juin 2008
    Messages
    140
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 140
    Par défaut
    Citation Envoyé par Gymble Voir le message
    La fonction ayant comme précondition que l'élément recherché doit être présent, dans ce cas a mon sens elle ne doit pas renvoyer un pointeur mais une référence.
    Pouvez-vous me justifier pourquoi svp ?

Discussions similaires

  1. Affectation de variable boucle FOR à l'intérieur d'une boucle WHILE
    Par Droïde Système7 dans le forum Débuter
    Réponses: 10
    Dernier message: 05/11/2007, 19h11
  2. : remplir des zones de texte avec une boucle For
    Par Haro_GSD dans le forum Access
    Réponses: 3
    Dernier message: 20/09/2005, 21h23
  3. Problème avec une DLL dans une boucle For
    Par BraDim dans le forum Langage
    Réponses: 5
    Dernier message: 20/09/2005, 12h22
  4. [batch] incrémentation dans une boucle for
    Par bart64 dans le forum Scripts/Batch
    Réponses: 4
    Dernier message: 08/09/2004, 20h05
  5. Réponses: 3
    Dernier message: 06/07/2004, 10h21

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