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 :

Question argument d'une fonction de type string


Sujet :

C++

  1. #21
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Par défaut
    L'inconvenient ici c'est que l'operateur d'affectation doit etre disponible.
    C'est pas très beau mais c'est pas mal comme astuce.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int fonction( int ancien, NouvelObjet & nouvelle fonctionnalité);
     
    inline int fonction( int ancien = 1) {
    	NouvelObjet tmp;
    	fonction (ancien, tmp);
    }
    Oui c'est l'une des solution que j'ai déjà évoqué.

  2. #22
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Par défaut
    Citation Envoyé par Montag Voir le message
    @corrector :
    Pourrais tu expliquer pourquoi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class B {};
     
    class A
    {
      void func( B & b = (B()=B()) );
    };
    fonctionne

    D'avance merci
    Tout simplement : dans ce cas d'espèce (classe B vide), puisqu'aucun opérateur d'affectation ("operator=") n'est déclaré, le compilateur va automatiquement générer une déclaration, équivalente à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class B {
    public:
    	B& operator= (const B&);
    };
    Remarque : si la classe B avait eu des membres, il en aurait été de même, du moment qu'aucun membre n'a un opérateur d'affectation prenant une référence non-constante (comme auto_ptr).

    Il faut noter à propos d'operator= que :
    1. c'est une fonction membre (forcément!)
    2. elle prend une référence constante
    3. elle renvoie une référence non-constante

    Le (1) implique qu'il est possible de l'invoquer sur une r-valeur, comme par exemple un temporaire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    B x;
    B().operator= (x); // fonction membre : ok
    B() = x; // exacactement la même chose, avec une syntaxe plus sympathique
    Le (2) permet évidement de passer une r-valeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    B().operator= (B());
    B() = B();
    Le (3) signifie que le résultat de l'application de cette fonction est une l-valeur, comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int val();
    int &ref();
    &val(); // erreur : val() est une r-valeur!
    val() = 4; // idem
    &ref(); // ok : ref() est une l-valeur
    ref() = 4; // idem
    Comme "B().operator= (B())" a le type référence non-constante sur B, c'est une l-valeur non-constante de B, donc on peut lier une référence non-constante sur B dessus :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    B &ref = (B().operator= (B()));
    B &r = (B() = B());
    donc le code est correct.

  3. #23
    Membre expérimenté
    Profil pro
    Inscrit en
    Août 2007
    Messages
    190
    Détails du profil
    Informations personnelles :
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Août 2007
    Messages : 190
    Par défaut
    Ok, je pense avoir compris, merci beaucoup.

  4. #24
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Par défaut
    Citation Envoyé par NiamorH Voir le message
    L'inconvenient ici c'est que l'operateur d'affectation doit etre disponible.
    C'est pas très beau mais c'est pas mal comme astuce.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int fonction( int ancien, NouvelObjet & nouvelle fonctionnalité);
     
    inline int fonction( int ancien = 1) {
    	NouvelObjet tmp;
    	fonction (ancien, tmp);
    }
    Oui c'est l'une des solution que j'ai déjà évoqué.
    C'est juste une façon de faire qui marche ici.

    En général, c'est plutôt sur des classes qui n'ont pas de notion de copie qu'on a besoin d'une telle chose, notamment ostrstream (traditionnellement)/ostringstream (actuellement).

    En traditionnel :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char *p = ((ostrstream&)(ostrstream() << ...)).str(); // cast ancien style, c'est du traditionnel!
    Mais ostrstream() est un temporaire, donc si on voulait sortir une classe Date :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    class Date;
    ostream& operator<< (ostream& os, const Date& d);
    ça marchait pas, évidement (référence liée à une r-valeur, vous connaissez la musique).

    Alors, on écrivait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char *p = ((ostrstream&)(ostrstream() << "" << date << ...)).str();
    parce que la sortie de const char * était
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ostream& ostream::operator<< (const char*)
    donc fonction membre, ça marche, << "" est un NOP, ça nous donne une l-valeur, super.

    Il se trouve, malheureusement, et bizarrement, que le comité, dont les voies sont vraiment impénétrables, a décidé qu'il valait mieux que cette fonction soit non-membre (ou alors, il a juste décidé ça à "pile-ou-face").

    Donc cette astuce ne fonction plus! Mais il reste heureusement des fonctions qui ne font rien et qui renvoient une référence sur *this, et d'habitude on utilise flush() pour ça (on en a souvent besoin sur un ostringstream, et flush ne fait strictement rien sur un ostringstream) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::string s = (static_cast<std::ostringstream&>(ostringstream().flush() << ...)).str();

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 641
    Par défaut
    Citation Envoyé par NiamorH Voir le message
    Absolument pas d'accord.
    Il existe des paramètres en entrée seule, en sortie seule, et en entrée/sortie.
    Un lien au hasard : http://www.jaylelillois.info/index.p...chap3&desc=iut
    A vrai dire, cette approche est, certes juste, si on ne se réfère qu'aux langages actuels, mais oublie le cheminement qui à mené à ce résultat.

    C'est un peu comme si on n'étudiait l'histoire de l'homme qu'à partir de 1800, en "oubliant" le Moyen-Age, l'antiquité et la préhistoire

    Avant que la notion de paramètres n'intervienne, on parlait de procédure, et non de fonction.

    Une procédure n'avait besoin que d'un nom, et ne nécessitait ni valeur de retour ni argument pour la simple et bonne raison que les langages qui les proposaient travaillaient... avec des variables globales.

    L'ensemble des procédures d'un programme avait donc tout loisir d'accéder à n'importe quelle variable globale, que ce soit en "lecture" ou en "écriture", avec tous les problèmes inhérents à cet état de fait.

    Une procédure poursuivait alors deux objectifs majeurs:
    • Eviter la recopie d'un code lourd en le remplaçant par l'appel de la procédure
    • permettre qu'un morceau de code puisse n'avoir qu'une responsabilité clairement définie.

    Par la suite, on (quand je dis "on", je parle des pères fondateurs de la programmation...) a pris conscience des problèmes inhérents à l'utilisation de variables globales, et on a décidé de les remplacer par des variables locales.

    Cela a impliqué que l'on réfléchisse à "d'autres moyens" en vue de mettre cette délégation de responsabilités en oeuvre, à savoir:
    • Etre en mesure de fournir des données nécessaire à la fonction
    • Etre en mesure de récupérer le résultat de la fonction

    La première étape est devenue le passage d'arguments, et la seconde la valeur de retour.

    Au début, il n'est question de passer les arguments que "en entrée", car le passage d'argument se fait "par valeur", et donc, une fonction ne pouvait donc fournir qu'un seul résultat, sous la forme de sa valeur de retour.

    Evidemment, pour "idéal" que soit cet état de fait du pur point de vue de la délégation des tâches, il présentait malgré tout des inconvénients majeurs, ne serait-ce que parce qu'il obligeait à écrire un code proche de
    Ou parce qu'il est difficile, quand tout est renvoyé par valeur de pouvoir déterminer si une structure est valide ou non lors du retour de la fonction.

    C'est alors que le C est apparu et a émis l'idée que
    Si nous fournissions comme argument la valeur de l'adresse mémoire à modifier au lieu de la valeur elle-même, nous pourrions "répercuter" les modifications apportées au sein d'une fonction appelée à la variable utilisée par la fonction appelante
    avec pour corolaire le fait que l'on pourrait très bien utiliser la valeur de retour pour signifier la réussite ou l'échec du traitement tout en obtenant la valeur valablement modifiée... Voire obtenir plusieurs valeurs modifiées (au détriment de la délégation des tâches).

    Le pointeur était né, et avec lui, la notion d'argument "d'entrée / sortie".

    Mais le pointeur n'est, à l'extrême, qu'un "sucre syntaxique" permettant de contourner une restriction au niveau des valeurs de retour

    Quand le C++ (mais pas seulement lui) est arrivé, il y avait suffisemment d'expérience de pointeurs mal gérés pour se rendre compte que le fait de travailler sur "l'adresse mémoire" à laquelle se trouve une variable peut être catastrophique, et l'idée suivante a donc été de "permettre de fournir un alias de la variable" comme argument de fonction (sous entendu que toute les modifications apportées à cet alias seraient répercutée sur la variable d'origine): cet alias de variable a pris le nom de référence en C++.

    Alors, bien sur, maintenant, tout le monde considère (du moins, quand on regarde les langages qui "ont le vent en poupe" actuellement) qu'un argument peut être en entrée seule, en entrée sortie ou en sortie...

    Mais, historiquement parlant, cela n'a absolument pas été toujours le cas.

    C'est la raison pour laquelle il est largement préférable de se rappeler que, bien qu'il soit *maintenant* possible d'envisager un argument "d'entrée / sortie", ça ne reste "qu'une facilité apportée par le langage" parce que le propre d'un argument a toujours été... de fournir à la fonction une valeur qu'elle est dans l'incapacité de déterminer d'elle-même.

    Une fois que tu accepte cette historique (très rapide) et que tu te rappelle d'où vient le C++, tu te rend compte que l'optique d'interdire une référence anonyme non constante ou un argument par défaut fourni sous la forme d'une référence non constante est la seule optique cohérente.

    Maintenant, d'autres optiques auraient pu être envisagées, mais elles l'auraient été au prix de l'oubli des racines du C++, et les philosophes (et les historiens) nous mettent en garde contre le danger de répéter les erreurs si on oublie trop son passé
    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

  6. #26
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Une fois que tu accepte cette historique (très rapide) et que tu te rappelle d'où vient le C++, tu te rend compte que l'optique d'interdire une référence anonyme non constante ou un argument par défaut fourni sous la forme d'une référence non constante est la seule optique cohérente.
    Je vois mal ce qu'il y a de "cohérent" à forcer le programmeur à utiliser les différentes astuces que j'ai donné pour obtenir une l-valeur non-constante sur un temporaire.

  7. #27
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 295
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 295
    Par défaut
    <rien à voir>
    Citation Envoyé par koala01 Voir le message
    Avant que la notion de paramètres n'intervienne, on parlait de procédure, et non de fonction.
    Dans le plus récent, je suis tombé sur le "cours d'algorithmie" de Stepanov (dispo en ligne gratuitement sur son site, avec les sources LaTeX/beamer et tout et tout). Bref, il emploie "procédure" pour les routines qui peuvent aussi altérer leurs arguments, et "fonctions" pour celles qui ne le peuvent/font pas -- et ce n'est pas du tout dit comme ça.
    </>
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  8. #28
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Par défaut
    Citation Envoyé par Luc Hermitte Voir le message
    <rien à voir>Bref, il emploie "procédure" pour les routines qui peuvent aussi altérer leurs arguments, et "fonctions" pour celles qui ne le peuvent/font pas -- et ce n'est pas du tout dit comme ça.
    </>
    <rien à voir/Ce n'est pas la terminologie Ada, ça? Jean-Marc?/

  9. #29
    Membre émérite Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Par défaut
    Je trouve cette terminologie bien plus cohérente, parce qu'on distingue bien les fonctions au sens mathématique ou de programmation fonctionnelle, des procédures.

    En l'occurence, un paramètre en entrée/sortie, c'est souvent pour éviter des recopies redondantes comme dit Koala, ou qu'on ne dispose pas d'un mécanisme simple pour créer des n-uplets.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 641
    Par défaut
    Citation Envoyé par corrector Voir le message
    Je vois mal ce qu'il y a de "cohérent" à forcer le programmeur à utiliser les différentes astuces que j'ai donné pour obtenir une l-valeur non-constante sur un temporaire.
    Le fait que, justement, c'est un objet temporaire que tu veux passer par référence...

    Il ne faut pas oublier que, en C++ (car l'optique est différente, en C# ou en java, par exemple), une référence est l'alias d'un objet existant, et qu'un objet est systématiquement détruit lorsque l'on quitte la portée dans laquelle il est déclaré.

    Une référence ne peut donc être valide que tant que l'objet auquel elle fait référence existe, et est utilisée pour deux raisons particulières qui sont:
    • d'éviter la copie de structures de grandes tailles
    • de permettre aux modifications apportées à la référence d'être répercutées vers l'objet d'origine existant dans la fonction appelante

    A partir du moment où tu ne poursuit pas ces deux objetctifs - et c'est le cas quand tu décide de fournir un argument facultatif sous la forme d'une référence, ne serait-ce que parce que cela implique la création d'un objet temporaire - je comprend parfaitement(et je laisse tout le monde libre de ne pas être d'accord avec moi) que le comité de normalisation n'ai pas jugé - jusqu'à présent - d'autoriser la modification de cet argument.

    Et la raison qui vient directement à l'esprit est, tout simplement, que tu ne dispose d'aucun moyen de savoir, lorsque tu implémente (toi, en tant que programmeur) ta fonction, si l'appel sera fait en passant une référence sur un objet existant ou si, au contraire, c'est l'argument par défaut (et donc temporaire) qui est utilisé.

    Bien sûr, tu peux encore "facilement" effectuer un test de valeur sur un membre de cet argument... Mais encore faut-il alors t'assurer que la valeur de l'argument par défaut n'apparaitra que... dans le cadre de l'argument par défaut, et ne puisse en aucun cas apparaitre lorsque l'argument est issu d'un objet existant.

    Autrement, tu risque, lors de l'implémentation de ta fonction, de partir du principe que "l'argument est tiré d'un objet existant en dehors de la fonction", et de fournir un comportement purement inutile (et potentiellement dangereux) si, d'aventure, ce n'est pas le cas.

    En gros, le comité n'a donc que pleinement appliqué le principe du rasoir d'Occam : si tu n'a pas besoin de quelque chose, ne le fais pas

    Cette optique est donc clairement cohérente avec les autres concepts, même si, de fait, d'autres optiques auraient peut être pu être tout aussi cohérentes
    Citation Envoyé par Luc Hermitte Voir le message
    <rien à voir>

    Dans le plus récent, je suis tombé sur le "cours d'algorithmie" de Stepanov (dispo en ligne gratuitement sur son site, avec les sources LaTeX/beamer et tout et tout). Bref, il emploie "procédure" pour les routines qui peuvent aussi altérer leurs arguments, et "fonctions" pour celles qui ne le peuvent/font pas -- et ce n'est pas du tout dit comme ça.
    </>
    Je conçois sans aucun problème que les termes "procédure" et "fonction" puissent être utilisés de manière pour ainsi dire indifférente lorsque l'on ne considère que l'algorithmique de manière générale.

    Cependant, un fait reste présent:

    Dans les langages qui utilisent les variables globales (COBOL avec sa procedure division (remarque qu'il ne s'agit pas de la fonction division ), le BASIC tel qu'il était présent dans les premier micro, ...) c'est le terme procédure qui est systématiquement utilisé, avec - effectivement - comme sous entendu qu'une procédure est susceptible de modifier n'importe quelle variable.

    Si, dans les langages qui n'utilisent pas exclusivement les variables globales, le terme procédure peut être utilisé, ce n'est que suite à l'amalgame entre les deux, mais en oubliant que si toute fonction (avec valeur de retour et arguments éventuels) est une procédure "particulière", une procédure au sens strict n'est pas forcément une fonction, car ne disposant pas forcément d'une valeur de retour et/ou d'arguments éventuels.

    Evidemment, on rencontre aussi des langages qui permettent d'office de considérer les argument comme étant "d'entrée / sortie" (sauf indication contraire), mais, de manière générale, on court moins de risque à penser sous la forme de
    un argument est d'entrée seule
    que sous celle de
    un argument est systématiquement d'entrée/sortie
    pour la simple et bonne raison que, si un argument est d'entrée / sortie et qu'on le considère comme étant d'entrée seule, au pire, on se restreint dans l'utilisation que l'on peut en faire, alors que si on considère un argument d'entrée seule comme étant un argument d'entrée/sortie, on s'ouvre la porte à des incompatibilités majeures
    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

  11. #31
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Par défaut
    Citation Envoyé par koala01 Voir le message
    bla bla bla ... moyen age ... bla bla bla ... inutile ... bla bla bla ... dangereux
    désolé, je ne suis pas convaincu.

Discussions similaires

  1. Réponses: 5
    Dernier message: 14/07/2010, 10h34
  2. Réponses: 9
    Dernier message: 10/05/2010, 21h35
  3. Réponses: 11
    Dernier message: 18/02/2007, 15h37
  4. Detecter le type d'un argument d'une fonction
    Par flipper203 dans le forum C++
    Réponses: 31
    Dernier message: 07/07/2006, 22h53
  5. passer FILE* en argument d une fonction
    Par Monsieur_Manu dans le forum C
    Réponses: 9
    Dernier message: 10/04/2003, 17h56

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