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

Langage C++ Discussion :

Surcharge et types entiers : laquelle devrait être choisie ?


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut Surcharge et types entiers : laquelle devrait être choisie ?
    Bonjour à tous,

    J'ouvre ce sujet à la suite de cette discussion et il ne traitera pas spécifiquement des enums :
    http://www.developpez.net/forums/d13...tout-a-fait-x/

    L'objectif de cette discussion n'est pas de savoir ce que dit la norme ni de ce que font actuellement les différents compilateurs, mais de savoir ce que vous trouvez logique comme comportements et pourquoi.

    Je vous proposes un code relativement simple contenant 6 appels à 3 fonctions surchargés :
    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
     
    #include<iostream>
     
     
    enum : short { val_short };
    enum : int   { val_int   };
     
     
    void short_or_int(short)
    { std::cout << "short"; }
     
    void short_or_int(int)
    { std::cout << "int"; }
     
     
    void int_or_long(int)
    { std::cout << "int"; }
     
    void int_or_long(long)
    { std::cout << "long"; }
     
     
    void long_or_longlong(long)
    { std::cout << "long"; }
     
    void long_or_longlong(long long)
    { std::cout << "longlong"; }
     
     
    int main()
    { 
        short var_short(0);
        int   var_int  (0);
     
        std::cout << "short_or_int     with var_short : "; short_or_int(var_short)  ; std::cout << std::endl;
        std::cout << "int_or_long      with var_short : "; int_or_long(var_short)   ; std::cout << std::endl;
        std::cout << "long_or_longlong with var_int   : "; long_or_longlong(var_int); std::cout << std::endl;
     
     
        std::cout << "short_or_int     with val_short : "; short_or_int(val_short)  ; std::cout << std::endl;
        std::cout << "int_or_long      with val_short : "; int_or_long(val_short)   ; std::cout << std::endl;
        std::cout << "int              with val_int   : "; long_or_longlong(val_int); std::cout << std::endl;
    }
    La question est de savoir ce que vous, développeur, attendez de ces appels de manière logique. Les réponses possibles sont :
    • L'appel ne doit pas compiler car aucune fonction ne correspond aux arguments.
    • L'appel ne doit pas compiler car il y a ambiguïté, plusieurs surcharges correspondent et aucune n'est à privilégier.
    • L'appel est valide car une seule surcharge correspond aux arguments.
    • L'appel est valide car plusieurs surcharges correspondent aux arguments mais une seule est à privilégier.

    L'objectif n'est pas de déterminer ce qu'il doit se passer en réalité, seulement de discuter de ce qui doit se passer de la manière la plus logique qui soit.

    Les trois premiers appels ne font pas usage des notions d'énumérations avec type sous-jacent explicitent du C++11, ils sont plus classiques mais cependant pas nécessairement dénués d'intérêt. Les trois derniers appels utilisent ces énumérations ce qui peut introduire des raisonnements différents.

    Je rappel juste deux choses :
    • Les types entiers ont en C++ un certain "ordre" entre eux, ce n'est pas nécessaire de rentrer dans les détails ici, il est juste à noter que short < int < long < long long, où < désigne cet ordre.
    • Les énumérations définissent un nouveau type, ainsi le type de val_short et val_int n'est ni short ni int, c'est autre chose (le type est non nommé ici). Le type sous-jacent (respectivement short et int) est le type des valeurs de l'énumération à l'intérieur de celle-ci, à l'extérieur ils ont le type définit par l'énumération.


    Bien entendu il sera intéressant dans la suite de la discussion de s'interroger sur ce qu'il doit se passer selon la norme et de le confronter aux premières intuitions.

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

    Informations professionnelles :
    Activité : aucun

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

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    short_or_int(var_short)
    devrait sortir "short", sans l'ombre d'un doute (c'est l'expression qui matche parfaitement)
    pourrait tout aussi bien sortir int que long, et ce, pour différente raisons:
    on rencontre beaucoup d'architectures sur lesquelles sizeof(long) == sizeof(int) (ce qui est parfaitement autorisé par la norme )
    si différence de taille il y a, le compilateur devrait choisir le type qui permet, pour l'architecture, de travailler le plus facilement.

    Par contre, il choisira souvent de travailler avec un int

    Je ne serais donc pas surpris de voir "int" en sortie
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    long_or_longlong(var_int);
    sensiblement la meme chose que juste avant, bien que sizeof(long) et sizeof(long long) ne soient généralement pas égaux...

    Je dirais : selon l'architecture : long en 32 bits, long long en 64 car ce sont "typiquement" les tailles qui s'adaptent le mieux aux différents accumulateurs

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    short_or_int(val_short)
    Je dirais short, c'est ce qui "matche" le mieux
    aussi bien int que long, avec une préférence pour int, pour les mêmes raisons que plus haut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    long_or_longlong(val_int);
    Encore une fois, cela dépendra de l'architecture...
    long en 32 bits, long long en 64 bits, pour les même raisons que plus haut
    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

  3. #3
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Salut,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    short_or_int(var_short)
    devrait sortir "short", sans l'ombre d'un doute (c'est l'expression qui matche parfaitement)
    Pour celui-ci je suis totalement d'accord.

    Citation Envoyé par koala01 Voir le message
    pourrait tout aussi bien sortir int que long, et ce, pour différente raisons:
    on rencontre beaucoup d'architectures sur lesquelles sizeof(long) == sizeof(int) (ce qui est parfaitement autorisé par la norme )
    si différence de taille il y a, le compilateur devrait choisir le type qui permet, pour l'architecture, de travailler le plus facilement.
    La réponse se trouve dans la norme (et à moins que je l'ai mal interprété je connais normalement les comportements attendus) mais je préfère exclure la norme de la discussion le plus possible pour le moment (l'objectif de la discussion est justement de voir si ces règles sont logiques ou non).

    Pour toi c'est donc un UB (enfin implementation-defined) : le compilateur a différentes possibilités alors il est "libre" de prendre celle qu'il veut (ou plus exactement de suivre l'architecture) ? Ce raisonnement me gène un peu dans le sens où l'algo de sélection des surcharges se doit d'être robuste parce qu’il le peut : le typage du C++ fait qu'il a bien assez d'information à la compilation pour dire si il peut ou non choisir une surcharge et ne pas jouer aux "dès".

    Citation Envoyé par koala01 Voir le message
    Je ne serais donc pas surpris de voir "int" en sortie
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    long_or_longlong(var_int);
    sensiblement la meme chose que juste avant, bien que sizeof(long) et sizeof(long long) ne soient généralement pas égaux...
    Le fait que tu arrives à la même conclusion me rassure, donc pour toi il est donc bien logique que le triplet short/int/long et int/long/long long se comporte de la même manière ? Sur ce point je suis assez d'accord.

    Citation Envoyé par koala01 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    short_or_int(val_short)
    Je dirais short, c'est ce qui "matche" le mieux
    Donc pour toi la conversion du type de l'enum vers le type sous-jacent de l'enum devrait être prioritaire devant tout les autres (excepté du type de l'enum vers le type de l'enum) ? Je trouve ceci logique aussi.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    La réponse se trouve dans la norme (et à moins que je l'ai mal interprété je connais normalement les comportements attendus) mais je préfère exclure la norme de la discussion le plus possible pour le moment (l'objectif de la discussion est justement de voir si ces règles sont logiques ou non).
    Ma référence à la norme avait juste pour but de rappeler que sizof(int) == sizeof(long) est parfaitement légal
    Pour toi c'est donc un UB (enfin implementation-defined) : le compilateur a différentes possibilités alors il est "libre" de prendre celle qu'il veut (ou plus exactement de suivre l'architecture) ? Ce raisonnement me gène un peu dans le sens où l'algo de sélection des surcharges se doit d'être robuste parce qu’il le peut : le typage du C++ fait qu'il a bien assez d'information à la compilation pour dire si il peut ou non choisir une surcharge et ne pas jouer aux "dès".
    Hummm... oui, et non...

    je comprends parfaitement ce qui te gène, mais je placerais plutôt ce genre de comportement dans la partie "implementation dependant".

    si c'est un "coup de dés", ils sont fortement pipés
    Donc pour toi la conversion du type de l'enum vers le type sous-jacent de l'enum devrait être prioritaire devant tout les autres (excepté du type de l'enum vers le type de l'enum) ? Je trouve ceci logique aussi.
    Cela me paraitrait effectivement logique
    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 Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Ma référence à la norme avait juste pour but de rappeler que sizof(int) == sizeof(long) est parfaitement légal
    Oui, c'est vrai aussi .

    Citation Envoyé par koala01 Voir le message
    Hummm... oui, et non...

    je comprends parfaitement ce qui te gène, mais je placerais plutôt ce genre de comportement dans la partie "implementation dependant".
    Je comprend pas ce que tu veux dire, mais ca me gène toujours, les dès ont beau être pipé, si je compile le même programme sur deux machines j'ai potentiellement deux comportements totalement différent (ça dépend ce que font les surcharges). Et ceci me gène en effet.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Je comprend pas ce que tu veux dire, mais ca me gène toujours, les dès ont beau être pipé, si je compile le même programme sur deux machines j'ai potentiellement deux comportements totalement différent (ça dépend ce que font les surcharges). Et ceci me gène en effet.
    Là aussi, je te comprends...

    Mais, sans faire référence à la norme pour le problème qui nous intéresse, je ne peux que constater qu'elle contient tant de comportements dépendants de l'implémentation que cela ne me gênerait pas outre mesure si c'en était un de plus
    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

  7. #7
    Membre Expert

    Avatar de germinolegrand
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2010
    Messages
    738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Octobre 2010
    Messages : 738
    Par défaut
    Pour ma part, qu'importe le détail, je m'attendrais en tout premier lieu à ce que les 3 résultats du bas soient strictement identiques aux 3 résultats du haut au minimum. Sinon l'intérêt de définir un type sous-jacent aux enums faiblement typées est grandement diminué.

    En effet, les enums faiblement typées ont pour intérêt majeur en C++11 de pouvoir être "confondues" avec leur type sous-jacent, les valeurs servant avant tout d'étiquettes facilitant l'écriture du code (et la généricité bien entendu).

    Pour les enums fortement typées, la question se pose moins puisqu'il faut de toute façon faire un cast explicite.

  8. #8
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Citation Envoyé par germinolegrand Voir le message
    Pour ma part, qu'importe le détail, je m'attendrais en tout premier lieu à ce que les 3 résultats du bas soient strictement identiques aux 3 résultats du haut au minimum. Sinon l'intérêt de définir un type sous-jacent aux enums faiblement typées est grandement diminué.
    Bien que les énumérations définissent dans tout les cas un nouveaux types, je peux comprendre cette idée : au sein de l'énumération le type des éléments est le type sous-jacent, donc en cas de conversion la priorité est laissé au type sous-jacent. C'est aussi quelque-chose que Koala01 trouverait naturel si j'ai bien compris ses propos.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Bien que les énumérations définissent dans tout les cas un nouveaux types, je peux comprendre cette idée : au sein de l'énumération le type des éléments est le type sous-jacent, donc en cas de conversion la priorité est laissé au type sous-jacent. C'est aussi quelque-chose que Koala01 trouverait naturel si j'ai bien compris ses propos.
    Ah oui, bien sur!

    Si on a quelque chose qui "matche" parfaitement, on l'utilise, sinon, on prend ce qui "demande le moindre effort".

    Pour ma part, je considère que ce qui demande "le moindre effort" consiste à promouvoir dans le type le plus adapté au processeur et est donc dépendant de la plateforme/ de l'architecture

    Enfin, si on ne dispose pas de la surcharge qui permet d'utiliser le type le plus adapté à l'architecture, le "moindre effort" consiste à promouvoir la valeur énumérée au rang d'entier (sous réserve toute fois que le type sous-jacent de la valeur énumérée soit "suffisamment petit" pour pouvoir être promu en entier )
    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

  10. #10
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Ah oui, bien sur!

    Si on a quelque chose qui "matche" parfaitement, on l'utilise, sinon, on prend ce qui "demande le moindre effort".
    Je suis totalement d'accord, et de manière global c'est bien l'esprit dans lequel se passe l'algo de sélection des surcharges (au sens large, pas juste avec mon exemple).

    Citation Envoyé par koala01 Voir le message
    Pour ma part, je considère que ce qui demande "le moindre effort" consiste à promouvoir dans le type le plus adapté au processeur et est donc dépendant de la plateforme/ de l'architecture
    Cette partie me gène vraiment même si je comprends bien ton idée. Cependant, le type int est par définition le type le mieux adapté à l'architecture (c'est normatif). Tu serais donc pour dire que si il n'y a pas correspondance exact alors la conversion prioritaire est vers int ?

    Citation Envoyé par koala01 Voir le message
    Enfin, si on ne dispose pas de la surcharge qui permet d'utiliser le type le plus adapté à l'architecture, le "moindre effort" consiste à promouvoir la valeur énumérée au rang d'entier (sous réserve toute fois que le type sous-jacent de la valeur énumérée soit "suffisamment petit" pour pouvoir être promu en entier )
    Si tu entends entier au sens int, alors c'est redondant, promouvoir en int c'est aussi promouvoir vers le type le plus adapté à l'architecture.

    Donc si je te suis bien (c'est pas garanti avec les posts croisés ), la logique serait, par ordre de priorité :
    • correspondance exacte
    • int : le type le plus adapté à l'architecture
    • autre ? dans un autre particulier ? tous au même niveau ?

    Et dans le cas des enums avec type sous-jacent explicite, tu mets la conversion énumération vers type sous-jacent à quel niveau ? Avant int ? Après int ? Au même niveau que int ?

Discussions similaires

  1. Réponses: 0
    Dernier message: 20/11/2014, 18h44
  2. [Fortran 90] Type entier non signé
    Par nnath dans le forum Fortran
    Réponses: 2
    Dernier message: 17/07/2006, 00h21
  3. Calculer la longueur d'une variable de type entier
    Par juliendeparis dans le forum C
    Réponses: 13
    Dernier message: 08/06/2006, 13h44
  4. [API] résultat d'un Insert sur un champs de type entier
    Par Popoyan dans le forum Bases de données
    Réponses: 3
    Dernier message: 05/06/2006, 14h16
  5. [LG]type entier
    Par fakroun dans le forum Langage
    Réponses: 3
    Dernier message: 20/11/2003, 23h39

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