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 :

optimisation, classes et operateurs


Sujet :

C++

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Espagne

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 25
    Points : 12
    Points
    12
    Par défaut optimisation, classes et operateurs
    Hello!

    J'ai fais une petite classe de Vecteurs3D. Elle a comme attributs prives un tableau de type vector de nom coord
    l'une des méthodes que j'ai fais calcule le produit vectoriel de deux vecteurs. J'ai fais une nouvelle instance puis initialisé ses
    valeurs mais cela pose un soucis lorsque je veux faire la surcharge d'opertateur* en effet, créer à chaque fois un tableau n'est pas une très bonne idée...

    j'espère que vous pourrez m'aider, car je ne suis pas convaincu de mon bricolage. De plus, il m'est conseiller d’éviter les setters donc j'imagine qu'il existe une solution.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    Vecteurs3D Vecteurs3D::prod_vec(Vecteurs3D const vec)const
    {
        Vecteurs3D resultat;
        resultat.set_coord(0, (coord[1]* vec.coord[2]) - (coord[2]*vec.coord[1]));
        resultat.set_coord(1, (coord[2]* vec.coord[0]) - (coord[0]*vec.coord[2]));
        resultat.set_coord(2, (coord[0]* vec.coord[1]) - (coord[1]*vec.coord[0]));
        return resultat;
    }
    Merci d'avance!

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 113
    Points : 32 958
    Points
    32 958
    Billets dans le blog
    4
    Par défaut
    Salut,

    tu es dans une fonction membre, pourquoi utiliser set_coord et non directement coord ?
    Tu passes ton paramètre par copie, utilise une référence. Dommage t'avais déjà pensé au const en plus. ^^
    Si tu dois faire 3 opérations, tu pourras difficilement faire mieux. Sauf à utiliser des intrinsics et SIMD.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Espagne

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 25
    Points : 12
    Points
    12
    Par défaut
    oui juste! J'y avais pas pensé je vais faire ça alors. Merci

  4. #4
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Espagne

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 25
    Points : 12
    Points
    12
    Par défaut
    Accessoirement, lors de la surcharge d'opérateur, comment différencier le produit vectoriel et le produit scalaire de vecteurs?
    voici mon prototype de produit scalaire:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const Vecteurs3D operator*(Vecteurs3D, Vecteurs3D const&);
    et sa definition:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    const Vecteurs3D operator*(Vecteurs3D v1, Vecteurs3D const& v2)
    {
        v1*=v2;
        return v1;
    }

  5. #5
    Expert éminent sénior

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 045
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 045
    Points : 11 368
    Points
    11 368
    Billets dans le blog
    10
    Par défaut
    Salut!
    Pour les vecteurs/points, je n'ai pas implémenté l'opérateur de multiplication avec un autre vecteur.
    J'ai implémenté 2 fonctions libres: cross et dot.
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

  6. #6
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 562
    Points : 7 628
    Points
    7 628
    Par défaut
    Bonjour,
    On ne peut distinguer les operations par leur type de retour. La définition de l'operateur^() pourrait fournir un produit vectoriel et operateur*() pour le produit scalaire, mais l'utilisation de fonction comme indiqué par dragonjoker59 est beaucoup moins opaque.

  7. #7
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 186
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 186
    Points : 17 126
    Points
    17 126
    Par défaut
    Et il y a le problème de la priorité des opérateurs.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  8. #8
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Salut,

    primo : tu as normalement un constructeur qui te permet de prendre les (3 ) valeurs qui doivent être représentées dans ton vecteur. Dés lors, pourquoi jouer avec set_coord (qui ne devrait pas exister: une classe à sémantique de valeur comme un vecteur mathématique est fondamentalement constant, à moins que l'on utilise un des opérande comme "vecteur destiné à contenir le résultat" d'une opération) au lieu du constructeur en queston
    ton code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Vecteurs3D Vecteurs3D::prod_vec(Vecteurs3D const vec)const
    {
        Vecteurs3D resultat;
        resultat.set_coord(0, (coord[1]* vec.coord[2]) - (coord[2]*vec.coord[1]));
        resultat.set_coord(1, (coord[2]* vec.coord[0]) - (coord[0]*vec.coord[2]));
        resultat.set_coord(2, (coord[0]* vec.coord[1]) - (coord[1]*vec.coord[0]));
        return resultat;
    }
    pourrait donc être remplacé par un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    Vecteurs3D Vecteurs3D::prod_vec(Vecteurs3D const vec)const
    {
        Vecteurs3D resultat((coord[1]* vec.coord[2]) - (coord[2]*vec.coord[1]), // X
                            (coord[2]* vec.coord[0]) - (coord[0]*vec.coord[2]), // Y
                            (coord[0]* vec.coord[1]) - (coord[1]*vec.coord[0]), //Z
                                     );
        return resultat;
    }
    qui te permettrait d'utiliser directement la liste d'initialisation utilisée dans le constructeur plutôt que de définir les valeurs des données membres à une valeur par défaut (0 ) avant de les modifier.

    C'est idiot, mais pour l'instant, tu fais en deux étapes ce que tu aurais pu faire en une seule

    Secundo, les deux règles de l'optimisation sont:
    • first rule (for anybody): Don't do this (première règle (pour tout le monde): ne fait pas cela)
    • second rule (for expert only): Don't do this yet (deuxième règle (pour les experts uniquement): ne fait pas encore cela).

    De prime abord, tu n'est pas encore expert. Je te conseille donc de t'en tenir à la première règle

    Tertio: Généralement, quand on envisage les opérateurs mathématiques, il y a systématiquement deux versions de ces opérateurs :
    1. la version "auto modifiante" qui modifie l'opérande de gauche (ex : +=, -=, *=, /=)
    2. la version "normale" qui renvoie une nouvelle donnée contenant le résultat de l'oparation (ex: +,-,*,/ )

    Ce que l'on fait généralement, c'est de définir le comportement des versions "auto modifiantes" et d'utiliser cette version sur une copie pour définir le comportement de la version "normale". Cela prend à peu de chose près la forme de
    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
     
    class MaClasse{
    public:
        /* on se fout plus ou moins de ce qu'il y a d'autre ;) */
        MaClass & operator += (MaClass const & other){
            // tous les calculs modifient les données membres de l'objet courant
            return *this;
       }
    };
    MaClass operator + (MaClass const & a, MaClass const & b){
        /* il y a moyen de simplifier le code, mais j'écrit la "version longue" pour
         * que ce soit plus compréhensible ;)
         */
        MaClass copy(a);
        return copy+=b;
    }
    Citation Envoyé par dalfab Voir le message
    Bonjour,
    On ne peut distinguer les operations par leur type de retour. La définition de l'operateur^() pourrait fournir un produit vectoriel et operateur*() pour le produit scalaire, mais l'utilisation de fonction comme indiqué par dragonjoker59 est beaucoup moins opaque.
    Hummm... ce n'est pas vraiment une bonne idée :

    La règle de base est de toujours respecter les conventions relatives au domaine pour lequel tu développe ta fonctionnalité!

    On assimile naturellement tous les opérateurs à une opération spécifique : + est l'addition, - est la soustraction, * est une multiplication, / est une division et ^ correspond à un xor bit à bit. Ce n'est pas moi qui le dit, c'est la convention à laquelle tout le monde est plus ou moins habitué

    Si tu remets cette convention en cause (par exemple, en faisant en sorte que - provoque l'addition ou que ^ provoque le produit scalaire), tu vas complètement dérouter tout le monde et les gens ne comprendront pas comment il se fait que le résultat de a-b ou de c^d (pour reprendre les deux exemples) ne correspond absolument pas à ce à quoi ils se seraient attendus.

    De plus, il faut savoir que le nom de la fonction n'a absolument aucune influence sur l'exécutable final: son seul but est de te permettre à toi (ou à tout développeur posant les yeux sur ton code) de savoir à quoi tu peux t'attendre lorsque tu appelle la fonction.

    Il n'y a -- du point de vue du fonctionnement de ton application -- absolument aucune différence entre le fait d'avoir une fonction cross() ou un opérateur ^() pour calculer le produit croisé, mais, pour toute personne posant les yeux sur ton code, il y a une des possibilités qui sera beaucoup plus claire à la simple lecture de ton code
    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

  9. #9
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par koala01 Voir le message
    La règle de base est de toujours respecter les conventions relatives au domaine pour lequel tu développe ta fonctionnalité !
    On assimile naturellement tous les opérateurs à une opération spécifique : + est l'addition, - est la soustraction, * est une multiplication, / est une division et ^ correspond à un xor bit à bit. Ce n'est pas moi qui le dit, c'est la convention à laquelle tout le monde est plus ou moins habitué
    En l'occurrence, le domaine est celui des opérations vectorielles. Je ne pense pas que ta définition de l'opérateur ^ y soit applicable.
    Ta définition, très répandue, sort justement du domaine, pour rejoindre celui du langage.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par oodini Voir le message
    En l'occurrence, le domaine est celui des opérations vectorielles. Je ne pense pas que ta définition de l'opérateur ^ y soit applicable.
    Ta définition, très répandue, sort justement du domaine, pour rejoindre celui du langage.
    C'est bien pour cela que je déconseille de définir un quelconque opérateur ^ dans le cadre d'opérations vectorielles: il n'y a pas de convention indiquant le fonctionnement de ce genre d'opérateur et donc toute personne posant les yeux sur le code aura tendance à se demander "mais que veut-il faire avec cet opérateur "

    En l'absence de convention, la personne lisant le code aura tendance à se rapprocher des conventions qu'elle connait, et risque de "retomber" sur une autre convention plus ou moins "classique" qui ne pourra que l'induire en erreur.

    Quoi qu'il en soit, si l'on est dans un domaine dans lequel il est "courant" d'utiliser l'opérateur ^ pour une représentation donnée, il peut s'avérer "cohérent" de définir cet opérateur, en veillant à respecter la convention d'usage, mais, si l'on est dan un domaine dans lequel il n'existe aucune convention pour un opérateur particulier, l'idée même de fournir "sa propre vision" de ce que peut faire cet opérateur ne peut qu'être source de confusion et de mauvaise utilisation

    Et c'est pour cela qu'il est largement préférable de définir une fonction portant un nom explicite (expliquant clairement ce qu'elle fait) -- qu'il s'agisse d'une fonction libre ou d'une fonction membre n'a pas tellement d'importance, même si l'idéal est toujours de privilégier une fonction libre -- plutôt que de définir un comportement quelconque pour un opérateur "non conventionnel"
    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. #11
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Espagne

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 25
    Points : 12
    Points
    12
    Par défaut tu as raison!
    Citation Envoyé par koala01 Voir le message
    Salut,

    primo : tu as normalement un constructeur qui te permet de prendre les (3 ) valeurs qui doivent être représentées dans ton vecteur. Dés lors, pourquoi jouer avec set_coord (qui ne devrait pas exister: une classe à sémantique de valeur comme un vecteur mathématique est fondamentalement constant, à moins que l'on utilise un des opérande comme "vecteur destiné à contenir le résultat" d'une opération) au lieu du constructeur en queston
    Hello,
    oui merci beaucoup! cependant le set-coord est vraiment nécessaire même si je ne peux encore dire pourquoi. Et oui, utiliser le constructeur est une meilleur solution à laquelle je n'y avais pas pensé merci

    Citation Envoyé par koala01 Voir le message
    Tertio: Généralement, quand on envisage les opérateurs mathématiques, il y a systématiquement deux versions de ces opérateurs :
    1. la version "auto modifiante" qui modifie l'opérande de gauche (ex : +=, -=, *=, /=)
    2. la version "normale" qui renvoie une nouvelle donnée contenant le résultat de l'oparation (ex: +,-,*,/ )

    Ce que l'on fait généralement, c'est de définir le comportement des versions "auto modifiantes" et d'utiliser cette version sur une copie pour définir le comportement de la version "normale". Cela prend à peu de chose près la forme de
    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
     
    class MaClasse{
    public:
        /* on se fout plus ou moins de ce qu'il y a d'autre ;) */
        MaClass & operator += (MaClass const & other){
            // tous les calculs modifient les données membres de l'objet courant
            return *this;
       }
    };
    MaClass operator + (MaClass const & a, MaClass const & b){
        /* il y a moyen de simplifier le code, mais j'écrit la "version longue" pour
         * que ce soit plus compréhensible ;)
         */
        MaClass copy(a);
        return copy+=b;
    }
    je connaissais déjà cela et c'est ce que j'utilise à chaque fois que je le peux. D’ailleurs si j'ai initié cette discussion c'était pour voir si l'on pouvait trouver une solution de ce style. Accessoirement dans la surcharge que tu as fais avec le "+" on peut, depuis c++2011 passer le 1er argument par copie puis ensuite la retourner ce qui s'avère plus efficace.

    Merci pour ta réponse je vais faire comme tu me l'as dit. Et je vais utiliser ^ pour le produit vectoriel malgré le débat qu'il y a la dessus.
    Cordialement, Dani

  12. #12
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par DanFire Voir le message
    Hello,
    oui merci beaucoup! cependant le set-coord est vraiment nécessaire même si je ne peux encore dire pourquoi. Et oui, utiliser le constructeur est une meilleur solution à laquelle je n'y avais pas pensé merci
    Non, absolument pas!
    set_coord ne fera que "désencapsuler" tes données en permettant à l'utilisateur de savoir quelque chose qu'il n'a absolument pas à savoir!

    De plus, on utilise souvent le vocable YAGNI en programmation (pour You Ain't Gonna Need It ou, si tu préfères en francais : Tu n'en a pas encore besoin): si tu ne sais pas encore dire pourquoi tu as besoin d'une fonction ou d'une donnée ou d'un type particulier, c'est que... tu n'en as pas besoin pour l'instant, et que tu peux donc attendre d'en avoir besoin pour le mettre en place
    je connaissais déjà cela et c'est ce que j'utilise à chaque fois que je le peux. D’ailleurs si j'ai initié cette discussion c'était pour voir si l'on pouvait trouver une solution de ce style. Accessoirement dans la surcharge que tu as fais avec le "+" on peut, depuis c++2011 passer le 1er argument par copie puis ensuite la retourner ce qui s'avère plus efficace.
    Ca, c'est anecdotique

    Merci pour ta réponse je vais faire comme tu me l'as dit. Et je vais utiliser ^ pour le produit vectoriel malgré le débat qu'il y a la dessus.
    Mais, tu me dis que j'ai raison, puis tu décide quand même de faire exactement le contraire de ce que je dis!!! faudrait savoir ce que tu veux

    Expose des fonction Vector cross(Vector const & left, Vector const & rigth) et Vector dot(Vector const & left, Vector const & right)( et encore, si tu en as besoin, ce qui risque effectivement d'être rapidement le cas); crées des fonctions membres qui modifient ton vecteur si tu le veux, mais ... Maintient la sémantique des opérateurs et n'essaye pas d'en proposer une qui ne fasse pas partie du domaine envisagé!!!
    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

  13. #13
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    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 275
    Points : 10 985
    Points
    10 985
    Par défaut
    J'avais fais des expérimentations pour voir comment minimiser les copies.
    Le code source est par là: https://github.com/LucHermitte/LucHe...etic-operators
    Les conclusions sont pour un billet à écrire, un jour.

    Note que si ton tableau est statique, que tu copies ou que tu déplaces, cela sera exactement du pareil au même -- le (N)RVO sera là quand il pourra. La différence se fait ressentir si tu as des allocations dynamiques derrière ton tableau. La seule façon de complètement minimiser les copies, c'est de passer par les expressions templates. Mais attention aux implémentations usuelles non compatibles avec auto (*), et attention aux multiplications matricielles, aux problèmes de cache, etc. Mieux vaut employer des libs matricielles qui ont déjà optimisé tout ça.

    (*) Bien que je pense que cette expérience règle le problème. N'hésitez pas à me faire des retours ici ou là bas.
    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...

  14. #14
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Espagne

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 25
    Points : 12
    Points
    12
    Par défaut Oula
    Les templates sont quelque chose que je n'ai pas encore vu en cours. Les setters sont inévitables pour une autre classe qui doit pouvoir modifier le vecteurs. Finalement, même si tu déconseille la surcharge de ^ au niveau ou j'en suis il m'est conseillé de le faire même si je concède que ce n'est pas correcte.

  15. #15
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par DanFire Voir le message
    Les templates sont quelque chose que je n'ai pas encore vu en cours.
    Ca viendra... ou non...
    Citation Envoyé par DanFire Voir le message
    Les setters sont inévitables pour une autre classe qui doit pouvoir modifier le vecteurs.
    A priori, non, et ce pour une excellente raison : si tu modifie n'importe quelle valeur interne de ton vecteur mathématique, tu obtiens bel et bien un vecteur totalement différent.

    Et c'est un raisonnement que l'on est parfaitement en droit de faire pour n'importe quel type présentant une sémantique de valeur.

    C'est pour cette raison que l'on insiste (pas assez fort à mon gout) sur le fait qu'un type ayant sémantique de valeur est généralement constant.

    Maintenant, j'admets volontiers qu'il est parfois utile, pour différentes raisons qui font que, de permettre certaines modifications au niveau des données internes de ce genre de types.

    Mais, quoi qu'il en soit, il est bien préférable de fournir un comportement clairement défini qui pourra s'assurer que les nouvelles valeurs seront correctes et cohérentes plutôt qu'un simple "setMachinChose" qui oblige l'utilisateur de la classe à se débrouiller, et qui, par conséquent, rend ta classe dépendante du bon vouloir / de l'état de concentration / de la logique de celui qui l'emploi.

    Tu dois en effet te dire que, si tu comptes sur l'utilisateur d'une fonctionnalité pour effectuer certaines vérifications qui seront de nature à garantir la cohérence de tes données, la bonne question à se poser n'est pas de savoir si l'utilisateur risque d'oublier ces vérifications, mais quand. Et la réponse pourra toujours être résumée en une phrase très simple : "au pire moment qui soit".

    Or, ton rôle en tant que développeur d'une fonctionnalité quelconque (qu'il s'agisse d'une fonction ou d'une classe) est, justement de te casser la tête afin de t'assurer:
    1. que l'utilisateur de ta fonctionnalité n'aura pas à le faire (se casser la tête)
    2. que l'utilisateur n'oubliera pas d'effectuer une vérification importante
    3. de limiter au maximum l'impact que pourrait avoir une erreur de la part de l'utilisateur lorsqu'il tente de l'utiliser

    les fonctions telles que setXXX sont bel et bien la preuve que tu refuse ta principale responsabilité en tant que développeur, car elles ne respectent pas tes deux premières obligations
    Finalement, même si tu déconseille la surcharge de ^ au niveau ou j'en suis il m'est conseillé de le faire même si je concède que ce n'est pas correcte.
    Ce qui fait que tu accepte délibérément d'avoir de fournir quelque chose de conceptuellement incorrect.

    Or, tu dois retenir que les conseilleurs ne sont jamais les payeurs, et que celui qui te l'aura conseillé ne sera pas là pour t'aider à ramasser les plâtres quand tout tombera en ruine!

    Et moi, je peux te garantir que, chaque fois que l'on prend une liberté de conception, on ne fait qu'ériger un mur dans lequel on ne manquera pas de foncer à la première occasion.

    Alors, même si je ne suis pas le payeur, même si je ne serai peut être pas là pour ramasser les plâtres, préfères tu suivre un conseil qui a de bonnes chances de te "pêter à la figure", ou celui qui, justement, te laissera le plus de chances de continuer à travailler "en toute sérénité"

    Car toute cette discussion ne se résume en définitive qu'à cela : il y a de "bons" conseils, et de mauvais... Quand "quelqu'un" a pris la peine de pointer du doigt les problèmes potentiels de chaque conseil, tu n'as plus qu'à choisir entre la voie de la raison ou l'envie de jouer à fanjo
    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

  16. #16
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 460
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 460
    Points : 6 064
    Points
    6 064
    Par défaut
    A propos de l'opérateur "^" et du produit vectoriel :

    En mathématiques, il est fréquent d'utiliser la notation "^" pour un produit vectoriel.
    Donc si les utilisateurs de la classe Vecteurs3D sont des matheux, je les vois mal interpréter verteur1^vecteur2 comme un ou bit à bit, surtout si ce genre d'expression apparaît dans un contexte où il serait plus logique de faire un produit vectoriel qu'un ou bit à bit.
    Donc je n'accroche pas au contre-argument du ou bit à bit.

    Par contre, comme le dit leternel, il faut penser à la priorité des opérateurs.
    Si je lis verteur1^vecteur2+vecteur3, en math, j'aurais tendance à l'interpréter comme (verteur1^vecteur2)+vecteur3.
    Par contre, en C++, il faut lire verteur1^(vecteur2+vecteur3).
    http://en.cppreference.com/w/cpp/lan...tor_precedence

    Pour cette raison, je déconseille aussi de définir "^" comme un produit vectoriel.

    (Cependant, tant que l'on est étudiant, il faut faire ce que dit le prof.)

  17. #17
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Wow, c'est un argument contre la surcharge de cet opérateur bien meilleur que "les gens ne vont pas comprendre".
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  18. #18
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par Pyramidev Voir le message
    (Cependant, tant que l'on est étudiant, il faut faire ce que dit le prof.)
    (nota: je t'ai mis plus 1 pour le reste )
    Ben, il faut encore voir à quel prof on se frotte, car, s'il est plutôt matheux, ton argument en ce qui concerne la priorité des opérateurs devrait pouvoir faire mouche, non

    Et, comme il n'est absolument pas possible de modifier la priorité des opérateurs (on peut juste modifier la manière dont ils produisent le résultat), l'opérateur ^ n'est absolument pas adapté.
    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

  19. #19
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Et, comme il n'est absolument pas possible de modifier la priorité des opérateurs (on peut juste modifier la manière dont ils produisent le résultat), l'opérateur ^ n'est absolument pas adapté.
    Cette phrase m'a fait penser à la création d'expressions template avec évaluation paresseuse.

    Voici un exemple brut (avec des problèmes dans tous les sens) en C++14.
    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
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -Ofast -std=c++14 -pedantic -fopenmp main.cpp -o main && ./main
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -Ofast -std=c++11 -pedantic -fopenmp main.cpp -o main && ./main
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -Ofast -std=c++98 -pedantic -fopenmp main.cpp -o main && ./main
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -Og -ggdb3 -std=c++14 -pedantic main.cpp -o main && valgrind ./main
     
    #include <iostream>
    #include <string>
    #include <vector>
    #include <algorithm>
    #include <limits>
    #include <type_traits>
    #include <memory>
     
     
    // Tag (for operator overload)
     
    class op_tag_t { };
     
     
    // vector3D<span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">^</span>class vector3D
    {
    public:tel que je l'ai utilisé
     
    	using tag = op_tag_t;
    	pas faire
    public:
     
    	std::string name;
     
    public:
     
    	vector3D(std::string const & name) : name(name) { }
     
    	// TODO
    };
     
     
    // Operations
     
    template <class T0, class T1>
    class op_t
    {
    public:
     
    	using tag = op_tag_t;
     
    public:
     
    	T0 a;
     
    	T1 b;
     
    public:
     
    	op_t(T0 const & a, T1 const & b) : a(a), b(b) { }
    };
     
    template <class T0, class T1>
    class plus_t : public op_t<T0, T1>
    {
    public:
     
    	using op_t<T0, T1>::op_t;
    };
     
    template <class T0, class T1>
    class cross_t : public op_t<T0, T1>
    {
    public:
     
    	using op_t<T0, T1>::op_t;
    };
     
    template <class T0, class T1>
    class dot_t : public op_t<T0, T1>
    {
    public:
     
    	using op_t<T0, T1>::op_t;
    };
     
     
    // make_op & operators
     
    template <class T0, class T1>
    auto make_plus(T0 const & a, T1 const & b)
    {
    	return plus_t<T0, T1>(a, b);
    }
     
    template <class T0, class T1>
    auto make_cross(T0 const & a, T1 const & b)
    {
    	return cross_t<T0, T1>(a, b);
    }
     
    template <class T0, class T1>
    auto make_dot(T0 const & a, T1 const & b)
    {
    	return dot_t<T0, T1>(a, b);
    }
     
    template <template <class T0, class T1> class OP_T, class T0, class T1>
    auto make_op(T0 const & a, T1 const & b)
    {
    	return OP_T<T0, T1>(a, b);
    }
     
    template
    <
    	class T0, class T1,
    	class T0_is_op_t = std::enable_if_t<std::is_same<typename std::decay_t<T0>::tag, op_tag_t>::value>,
    	class T1_is_op_t = std::enable_if_t<std::is_same<typename std::decay_t<T1>::tag, op_tag_t>::value>
    >
    auto operator +(T0 const & a, T1 const & b)
    {
    	return make_plus(a, b);
    }
     
    template
    <
    	class T0, class T1,
    	class T0_is_op_t = std::enable_if_t<std::is_same<typename std::decay_t<T0>::tag, op_tag_t>::value>,
    	class T1_is_op_t = std::enable_if_t<std::is_same<typename std::decay_t<T1>::tag, op_tag_t>::value>
    >
    auto operator *(T0 const & a, T1 const & b)
    {
    	return make_cross(a, b);
    }
     
    template
    <
    	class T0, class T1,
    	class T0_is_op_t = std::enable_if_t<std::is_same<typename std::decay_t<T0>::tag, op_tag_t>::value>,
    	class T1_is_op_t = std::enable_if_t<std::is_same<typename std::decay_t<T1>::tag, op_tag_t>::value>
    >
    auto operator ^(T0 const & a, T1 const & b)
    {
    	return make_dot(a, b);
    }
     
     
    // Display
     
    void display_op(vector3D const & v3D)
    {
    	std::cout << v3D.name;
    }
     
    template <class T0, class T1>
    void display_op(plus_t<T0, T1> const & op)
    {
    	std::cout << "(";
    	display_op(op.a);
    	std::cout << " + ";
    	display_op(op.b);
    	std::cout << ")";
    }
     
    template <class T0, class T1>
    void display_op(cross_t<T0, T1> const & op)
    {
    	std::cout << "(";
    	display_op(op.a);
    	std::cout << " x ";
    	display_op(op.b);
    	std::cout << ")";
    }
     
    template <class T0, class T1>
    void display_op(dot_t<T0, T1> const & op)
    {
    	std::cout << "(";
    	display_op(op.a);
    	std::cout << " . ";
    	display_op(op.b);
    	std::cout << ")";
    }
     
    template <class T0, class T1, class T2>
    void display_op(dot_t<plus_t<T0, T1>, T2> const & op)
    {
    	display_op(make_plus(op.a.a, make_dot(op.a.b, op.b)));
    }
     
    template <class T0, class T1, class T2>
    void display_op(dot_t<T2, plus_t<T0, T1>> const & op)
    {
    	display_op(make_plus(make_dot(op.a, op.b.a), op.b.b));
    }
     
     
    int main()
    {
    	vector3D a("a");
    	vector3D b("b");
    	vector3D c("c");
     
    	display_op(a + b ^ c); std::cout << std::endl; // (a + (b . c))
     
    	display_op(a ^ b + c); std::cout << std::endl; // ((a . b) + c)
     
    	return 0;
    }
    Je passe sur les problème habituels des expressions templates faites à la main (le code présenté se veut être quasiment minimal et ne s'occupe pas de la) :
    - gestion des différents cas / opérateurs / opérations
    - gestion des temporaires
    - gestion des erreurs
    - ...

    Questions fonctionnalités, voici le main extrait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    vector3D a("a");
    vector3D b("b");
    vector3D c("c");
     
    display_op(a + b ^ c); std::cout << std::endl; // (a + (b . c))
    display_op(a ^ b + c); std::cout << std::endl; // ((a . b) + c)
    On remarque que a + b ^ c donne bien (a + (b . c)),
    et que a ^ b + c donne bien ((a . b) + c)

    Dans le code exemple, je ne "gère" que les expressions avec un niveau de + et de ^.
    C'est volontaire pour la simplicité du code. Parcourir l'expression pour balancer l'arbre de type en fonction de ^ requiert un haut niveau de meta-programmation et faire ça avec de la surcharge (comme dans l'exemple) n'apporte que des appels de fonctions ambigües.

    Un des point les plus génant est la gestion des parenthèses. En effet si l'utilisateur demande (a + b) ^ c, on va quant même générer l'expression (a + (b ^ c)). On ne peut pas surcharger ( et ) en C++. Pour gérer ce cas, on devra demander à l'utilisateur d'écrire f(a + b) ^ c et gérer la priorité des différents types à la main (?)

    Note "amusante" :
    Dès le début, le compilateur nous prévient de la priorité des opérateurs et nous suggère de mettre des parenthèses (sans savoir si le développeur pensait faire la bonne chose ou pas)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    warning: suggest parentheses around arithmetic in operand of ‘^’ [-Wparentheses]
      display_op(a + b ^ c); std::cout << std::endl;
                   ^
    warning: suggest parentheses around arithmetic in operand of ‘^’ [-Wparentheses]
      display_op(a ^ b + c); std::cout << std::endl;
    Je n'ai pas vraiment d'avis réfléchi mais ma pseudo-conclusion est : ne faites pas ça !
    Même si on pouvait changer la priorité des opérateurs à grand coup d'expressions template et d'évaluation paresseuse, il me paraît compliqué de gérer les parenthèses (correctement) et, de toute façon, le compilateur (tel que je l'ai utilisé) émet des warnings.

  20. #20
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Oui, en créant son propre parser d'expression, il y a toujours moyen de faire quelque chose... Mais bon, je sais pas: ca ne te parait pas extrême, à toi, de créer un parser d'expression rien que pour utiliser un opérateur qui aurait tout aussi bien pu prendre la forme d'une fonction dont le nom est explicite et qui ne pose pas de problème de priorité
    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

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Optimisation de surcharge d'operateurs de class
    Par shadowsam dans le forum Général Python
    Réponses: 2
    Dernier message: 05/03/2009, 10h59
  2. [POO] classe vect operateur []
    Par chris_013 dans le forum C++
    Réponses: 2
    Dernier message: 15/10/2008, 14h26
  3. [debutant] Utilisation de l'operateur = dans une classe
    Par Battosaiii dans le forum Débuter
    Réponses: 8
    Dernier message: 11/11/2005, 00h01
  4. Optimisation PHP4 lorsque l'on utilise presque des classes
    Par ouioui2000 dans le forum Langage
    Réponses: 2
    Dernier message: 11/10/2005, 18h05
  5. [Surcharge]Operateur<< avec une classe maison
    Par KeNnEdY dans le forum C++
    Réponses: 6
    Dernier message: 14/09/2005, 16h51

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