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 :

Convertion implicite des paramètres des constructeurs d'une classe.


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Par défaut Convertion implicite des paramètres des constructeurs d'une classe.
    Bonjour.

    Problème dont la réponse doit être toute simple mais que je ne connais pas.

    J'ai une classe Color.

    Elle possède les constructeurs :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Color(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha = 255);
    Color(float red, float green, float blue, float alpha = 1.f);
    Pour le premier constructeur, on s'attend à ce que la couleur soit construite comme ça : Color red(255, 0, 0);

    Dans le deuxième, elle est construite comme ça : Color red(1.f, 0.f, 0.f);

    Or, dans le premier cas Color red(255, 0, 0), le compilateur ne sais pas quelle constructeur utiliser :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    1>..\src\ExEngine\Graphics\Color.cpp(17) : error C2668: 'ex::Color::Color' : ambiguous call to overloaded function
    1>        D:\Excellence\include\ExEngine/Graphics/Color.hpp(15): could be 'ex::Color::Color(float,float,float,float)'
    1>        D:\Excellence\include\ExEngine/Graphics/Color.hpp(14): or       'ex::Color::Color(sf::Uint8,sf::Uint8,sf::Uint8,sf::Uint8)'
    1>        while trying to match the argument list '(int, int, int)'
    Quelle solution puis-je utiliser pour que le compilo puisse deviner quel constructeur utiliser, suivant que j'utilise des uchar ou des float?

    Je bosse sous Visual Studio 2008

  2. #2
    Membre Expert
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Par défaut
    Un petit coup de SFINAE:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template<class T>
    Color( T r, T g, T b, T alpha = T(255), typename boost::enable_if< boost::is_integral<T> >::type* = 0 );
     
    template<class T>
    Color( T r, T g, T b, T alpha = T(1.f), typename boost::enable_if< boost::is_floating_point<T> >::type* = 0 );

  3. #3
    Membre confirmé
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Par défaut
    Il n'y a pas un moyen plus simple et sans passer par boost? :/

  4. #4
    Membre Expert
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Par défaut
    Non. Et va falloir arreter avec cette phobie de boost.

  5. #5
    Membre confirmé
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Par défaut
    J'ai trouvé une autre solution :

    Je déclare deux classe, une Coloruc avec un constructeur Coloruc (unsigned char r, unsigned char g, unsigned char b, unsigned char a), ma classe Color avec un constructeur Color(const Coloruc & color) et son constructeur en float, et avec la conversion implicit, il accept que je fasse Color red(255, 0, 0).

    Ca impose une construction supplémentaire, mais ça marche.

  6. #6
    Membre Expert
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Par défaut
    I beg to differ :

    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
    struct foo
    {
      foo( int,int,int,int ) { cout << "foo\n"; }
    };
     
    struct bar
    {
      bar(float,float,float,float) { cout << "bar\n"; }
      bar( foo const& ) { cout << "foo in bar\n"; }
    };
     
    int main()
    {
      bar b(1,2,3,4);
      bar c(1.f,2.f,3.f,0.f);
    }
    Sortie :
    bar
    bar

  7. #7
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2011
    Messages
    59
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mars 2011
    Messages : 59
    Par défaut
    Citation Envoyé par Spidyy Voir le message
    J'ai trouvé une autre solution :

    Je déclare deux classe, une Coloruc avec un constructeur Coloruc (unsigned char r, unsigned char g, unsigned char b, unsigned char a), ma classe Color avec un constructeur Color(const Coloruc & color) et son constructeur en float, et avec la conversion implicit, il accept que je fasse Color red(255, 0, 0).

    Ca impose une construction supplémentaire, mais ça marche.
    Salut

    il y a les named constructors :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public :
    static Color Color::Coloruc(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha = 255);
    static Color Color::Colorf(float red, float green, float blue, float alpha = 1.f);
    pour garder une seule classe Color

  8. #8
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Citation Envoyé par Joel F Voir le message
    Et va falloir arreter avec cette phobie de boost.
    Si vraiment ça vous donne des boutons, essayez C++0x
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    #include <type_traits>
    struct Color
    {
       template<class T>
        Color( T , T , T , T  = T(255), typename std::enable_if< std::is_integral<T>::value >::type* = 0 )
        {
        }
     
        template<class T>
        Color( T , T , T , T  = T(1.f), typename std::enable_if< std::is_floating_point<T>::value >::type* = 0 )
        {
        }
    };

  9. #9
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Ceci étant dit, l'approche générique fait perdre la coercition qui peut être parfois utile (voire voulue) : Color c(300,-1,0); ne génère plus de warning par exemple.

    Ca peut être encore plus gênant avec la question de dragonjoker59 relative aux arguments différents. Avec un type entier, les préconditions vont être [0;255], avec un type réel, elles vont être de [0;1]. La définition du contrat devient plus laborieuse (mais en contre partie tire probablement l'avantage de plus de généricité).

    Au final, il n'y a plus qu'un seul constructeur qui doit alors probablement passer par une phase de normalisation des arguments. Il devient plus difficile de dire si Color c(1., 25, 0.5) est voulu ou s'il s'agit d'une erreur de frappe pour Color c(1., 2.5, 0.5)

    Bref, cette approche générique m'embête. Si elle peut être séduisante par certains points (renforcement du typage de chaque argument + 'abstraction/conceptualisation' de l'interface supérieure), je crains qu'elle ne finisse par être génératrice d'erreurs dans du code lambda.

    Finalement, je ne sais encore quelle solution j'adopterais dans un tel cas :
    => utiliser une seule signature à 3/4 arguments et l'autre avec une conversion explicite :
    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
    struct Color
    {
        Color( unsigned char , unsigned char , unsigned char , unsigned char  = unsigned char(255))
        {
        }
     
     
        struct RGB
        {
            RGB(float,float,float,float = 1.f){}
        };
        Color( RGB)
        {
        }
    };
     
    int main()
    {
        Color c(30,1,43);
        Color c2(Color::RGB(1.f,1.f,1.f));
        return 1;
    }
    => utiliser l'idiom du named constructeur comme proposé par kessoufi :
    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
    struct Color
    {
    private:
        Color( unsigned char , unsigned char , unsigned char , unsigned char  = unsigned char(255))
        {
        }
        Color(float, float,float,float=1.)
        {
        }
    public:
       static Color create_rgb(unsigned char a, unsigned char b,unsigned char c,unsigned char d=255)
       {return Color(a,b,c,d);}
       static Color create_hsv(float a, float b,float c,float d=1.)
       {return Color(a,b,c,d);}
    };
     
    int main()
    {
        Color c = Color::create_rgb(30,1,43);
        Color c2 = Color::create_hsv(1.,1.,1.);
        return 1;
    }
    Ou utiliser la coercition que sur les types integer et la surcharge pour les types flottants :
    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
    struct Color
    {
    public:
        Color( int , int , int , int  = int(255))
        {
        }
        Color(float, float,float,float=1.)
        {
        }
        Color(double, double,double,double=1.)
        {
        }
    public:
        Color(long double, long double,long double,long double=1.)
        {
        }
    public:
    };
     
    int main()
    {
        Color c(30,1,43);
        Color c2(1.,1.,1.);
        Color c3(1.f,1.f,1.f);
        return 1;
    }

    En écrivant ces lignes, j'ai tendance à pencher pour la première solution (avec RGB explicite).

  10. #10
    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
    Une autre manière de résoudre l'ambiguïté est de passer des constantes char, et non des constantes int, à un constructeur prenant des char :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    struct bar
    {
      bar(float,float,float,float) { cout << "float\n"; }
      bar( char, char, char,char ) { cout << "uchar\n"; }
    };
     
    int main()
    {
      bar b('\1','\2','\16','\4');
      bar c(1.f,2.f,3.f,0.f);
     
    }
    uchar
    float
    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.

  11. #11
    Membre Expert
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Si vraiment ça vous donne des boutons, essayez C++0x
    Jusqu'au bout alors

    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
     
    #include <type_traits>
    struct Color
    {
       template<class T
                   , class Enable = typename std::enable_if< std::is_integral<T>::value >::type
                   >
        Color( T , T , T , T  = T(255) )
        {
        }
     
        template<class T               
                    , class Enable = typename std::enable_if< std::is_floating_point<T>::value >::type
                   >
        Color( T , T , T , T  = T(1.f) )
        {
        }
    };

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 11
    Dernier message: 26/02/2015, 01h20
  2. Conventions en matière de contrôle des paramètres en entrée d'une fonction
    Par Jimalexp dans le forum Algorithmes et structures de données
    Réponses: 0
    Dernier message: 16/01/2009, 20h21
  3. Réponses: 8
    Dernier message: 17/04/2007, 11h35
  4. passer des paramétres d'un formulaire à une requête
    Par lasmarmann dans le forum Access
    Réponses: 1
    Dernier message: 24/10/2006, 09h37
  5. Retrouver les valeurs des paramètres des fonctions d'une DLL
    Par Bernard Martineau dans le forum Langage
    Réponses: 6
    Dernier message: 08/11/2005, 10h42

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