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 :

templates et opérateurs.


Sujet :

C++

  1. #1
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut templates et opérateurs.
    Bonjour à tous.
    J'ai déclaré un opérateur comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template<typename Type, unsigned int L, unsigned int C, bool Static>
    bool operator!=(const typename Matrix<Type,L,C,Static>::const_iterator& first, const typename Matrix<Type,L,C,Static>::const_iterator& second);
    Et dans le main :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <iostream>
    #include "Matrix.hpp"
     
    typedef Matrix<float, 4,4, true> Mat;
     
    int main()
    {
        Mat m;
        Mat::const_iterator it=m.begin();
        Mat::const_iterator it2=m.end();
        bool t= it!=it2);
        return 0;
    }
    Et j'obtiens :

    error: no match for 'operator!=' in 'it != it2'

    Je ne comprends pas pourquoi il n'appelle pas ma fonction template.
    Cependant, ceci marche :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bool operator!=(const Matrix<float,4,4,true>::const_iterator& first, const Matrix<float,4,4,true>::const_iterator& second);
    Quelqu'un sait-il pourquoi ma fonction ne marche pas ?

    Je compile avec Mingwgcc4.5 avec -std=c++0x, -pedantic-errors -Wall ...

    Merci à tous.

  2. #2
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Cependant, ceci marche :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template<typename T>
    bool operator!=(const T& first, const T& second);
    Mais n'est pas vraiment pratique...

  3. #3
    screetch
    Invité(e)
    Par défaut
    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
    template< int i >
    struct A
    {
        struct B
        {
        };
    };
     
     
    template< int i > bool operator !=(const typename A<i>::B& b1, const typename A<i>::B& b2) { return false; }
    template< int i > bool operator !=(const A<i>& a1, const A<i>& a2) { return false; }
     
    int main()
    {
        A<0> a1;
        A<0> a2;
        a1 != a2;
     
        A<0>::B b1;
        A<0>::B b2;
        b1 != b2;
    }
    ca ne marche pas car dans le cas d'une classe "nested", le compilateu n'arrive pas a déduire automatiquement les arguments.
    La seule solution que je vois comme ca (mais je suis fatigué alors y'en a ptetre d'autres ) c'est de mettre l'opérateur != dans la classe au lieu de la fonction libre

  4. #4
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Ok, merci screetch. J'avais espérer qu'il y aurait une autre solution...
    Je me demande quand même pourquoi le compilateur n'est pas capable de trouver la "nested". Bizarre de n'avoir aucun warning comme quoi la fonction ne sert à rien telle quel.

  5. #5
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par NoIdea Voir le message
    Ok, merci screetch. J'avais espérer qu'il y aurait une autre solution...
    Je me demande quand même pourquoi le compilateur n'est pas capable de trouver la "nested". Bizarre de n'avoir aucun warning comme quoi la fonction ne sert à rien telle quel.
    Il suffit de se demander : est-ce que toi, a qui on présente une classe X, est capable de dire que X est nested ou pas ? C'est la question à laquelle le compilateur doit répondre, parce qu'il n'a aucunement conscience du type de l'itérateur que tu lui passe. Ou plus exactement, découvrant un itérateur, il n'a aucune chance de savoir que cet itérateur est nested dans une classe matrice (réflexion : et si tu fais typedef std::vector<float>::iterator iterator dans ta classe matrice ?).
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  6. #6
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Il suffit de se demander : est-ce que toi, a qui on présente une classe X, est capable de dire que X est nested ou pas ?
    Si j'ai tous les headers, il me semble que oui.

    Je ne vois pas la difficulté supplémentaire (pour le compilateur) entre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template<typename T> struct f;
    template<typename R, typename O, typename ...Args>
    struct f<R(F::*O)(Args...)>
    {};
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template<typename T> struct f;
    template<typename O, typename I>
    struct f<O::I>
    {};
    Je parle avec des struct et non des fonctions mais cela ne change pas grand chose (du moins je pense).

  7. #7
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par NoIdea Voir le message
    Je ne vois pas la difficulté supplémentaire (pour le compilateur) entre :
    Ton problème initial est de déduire des arguments template, tu montres ici qqch où il n'y a pas de déduction. Je ne vois pas le rapport.

    Si tu as
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    template <typename T> struct St1 {
       typedef int Td1;
    };
     
    template <typename T>
    bool f(St<T>::Td1);
    Je suppose que tu comprends que ce n'est pas possible de déduire T lors d'un appel à f. Et bien on ne s'est pas amusé à déterminer les règles qui permettraient de faire la différence entre ce cas là et un cas où un type imbriqué permet de connaître les paramètres templates du type dans lequel il est imbriqué (entre autre parce comme déjà indiqué les spécialisations viennent encore compliquer les choses) et la règle est qu'il n'y a pas de déduction.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  8. #8
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par NoIdea Voir le message
    Si j'ai tous les headers, il me semble que oui.

    Je ne vois pas la difficulté supplémentaire (pour le compilateur) entre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template<typename T> struct f;
    template<typename R, typename O, typename ...Args>
    struct f<R(F::*O)(Args...)>
    {};
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template<typename T> struct f;
    template<typename O, typename I>
    struct f<O::I>
    {};
    Je parle avec des struct et non des fonctions mais cela ne change pas grand chose (du moins je pense).
    On va le présenter autrement :

    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
     
    struct X
    {
      typedef std::vector<unsigned char> collection_type;
     
      collection_type m_x;
    };
     
    struct Y
    {
      typedef std::vector<unsigned char> collection_type;
     
      collection_type m_y;
    }; 
     
    template <class T>
    void do_something(const typename T::collection_type& collection)
    {
    ...
    }
     
    void f(const std::vector<unsigned char>& v)
    {
      do_something<?>(v);
    }
    Que dois-je mettre à la place de "?" ?
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  9. #9
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Dans le cas ou il ne s'agit pas d'un typedef, le type réel de l'iterator est
    Object<templates>::iterator.
    Iterator, lorsqu'il est déclaré est déclaré avec les templates.
    Donc, avec :

    template<typename T>
    void do_something(const typename T::iterator&);
    Le type de T est évidemment à chercher dans la déclaration de l'itérator. Il s'agit de Object<templates>.

    Dans le cas de typedef, c'est un peu plus dur si on ne peut regarder la déclaration (je ne sais comment un typedef fonctionne, mais j'imagine l'équivalent d'un #define). Il ne faut pas que le préprocesseur remplace la
    X::iterator par int* (par exemple). Si on considère donc que le type reste X::iterator, il me semble que le compilateur pourrait déduire que T=X.
    Après, sa ne marche pas comme sa et c'est bien dommage mais faut faire avec...

    Juste une autre question, je me suis toujours demandé pourquoi les iterator STL n'avaient pas un operator const_iterator. Est-ce une erreur de l'introduire dans mes iterator ?

  10. #10
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par NoIdea Voir le message
    Dans le cas ou il ne s'agit pas d'un typedef, le type réel de l'iterator est
    Object<templates>::iterator.
    Iterator, lorsqu'il est déclaré est déclaré avec les templates.
    Donc, avec :

    template<typename T>
    void do_something(const typename T::iterator&);
    Le type de T est évidemment à chercher dans la déclaration de l'itérator. Il s'agit de Object<templates>.

    Dans le cas de typedef, c'est un peu plus dur si on ne peut regarder la déclaration (je ne sais comment un typedef fonctionne, mais j'imagine l'équivalent d'un #define). Il ne faut pas que le préprocesseur remplace la
    X::iterator par int* (par exemple). Si on considère donc que le type reste X::iterator, il me semble que le compilateur pourrait déduire que T=X.
    Après, sa ne marche pas comme sa et c'est bien dommage mais faut faire avec...
    typedef défini un alias, ce qui est, effectivement, grosso-modo une sorte de #define.

    Du coup, tu te retrouverais avec une fonctionnalité qui marche dans certains cas, et pas dans d'autres, et ce de manière inégale sur les différentes librairies standard (certaines implémentent les itérateurs en nested classes, d'autres non).

    Et quand bien même iterator serait une nested class, la fonctionnalité serait bancale quand même. sans apporter vraiment grand chose : si on passe en paramètre un type d'itérateurs, alors on travaille sur des itérateurs, pas sur la classe dans laquelle le type itérateur est défini. Si on a besoin d'en savoir plus sur les itérateurs en question, il y a toujours std::iterator_traits<> pour s'en sortir. Faire la décision du comportement sur le type propriétaire de la nested class, c'est volontairement se limiter dans la généricité de son code - et je n'en voit pas trop l'intérêt.

    Citation Envoyé par NoIdea Voir le message
    Juste une autre question, je me suis toujours demandé pourquoi les iterator STL n'avaient pas un operator const_iterator. Est-ce une erreur de l'introduire dans mes iterator ?
    Au niveau de la norme, aucun opérateur conversion implicite n'est défini dans la librairie standard. Ils sont de toute manière dangereux, et moins maitrisable qu'un constructeur par copie (notamment lorsqu'on écrit des choses un peu compliquées). Ceci dit, les iterators de certains conteneurs (je n'ai pas vérifié pour tous) sont convertibles en const_iterator, donc je ne vois pas vraiment l'intérêt de leur ajouter un operator const_iterator.

    Dans la librairie standard fournie avec g++, ces itérateurs sont basés sur __normal_iterator (dans /usr/include/c++/4.4.3/bits/stl_iterators.h); qui définit le constructeur suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
          // Allow iterator to const_iterator conversion
          template<typename _Iter>
            __normal_iterator(const __normal_iterator<_Iter,
                              typename __enable_if<
                   (std::__are_same<_Iter, typename _Container::pointer>::__value),
                          _Container>::__type>& __i)
            : _M_current(__i.base()) { }
    Je le concède, c'est peu lisible, mais ça permet d'écrire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    std::vector<X>::iterator mutable_iterator = v.begin();
    std::vector<X>::const_iterator constant_iterator = mutable_iterator;
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  11. #11
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Du coup, tu te retrouverais avec une fonctionnalité qui marche dans certains cas, et pas dans d'autres, et ce de manière inégale sur les différentes librairies standard (certaines implémentent les itérateurs en nested classes, d'autres non).
    C'est pour sa qu'il faudrait alors que les typedefs soient traités plus tard.

    Et quand bien même iterator serait une nested class, la fonctionnalité serait bancale quand même. sans apporter vraiment grand chose : si on passe en paramètre un type d'itérateurs, alors on travaille sur des itérateurs, pas sur la classe dans laquelle le type itérateur est défini.
    Je ne suis pas d'accord : bien que l'on ne travaille pas sur la classe "extérieure", il peut être intéressant de limiter le type d'itérateurs :
    Dans mon exemple, ceci est très clair :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template<typename T>
    bool operator!=(const T& first, const T& second);
    Ceci compile. Mais quel risque : Le compilateur risque de me mettre qu'il ne sait qu'elle fonction choisir pour l'opérateur != pour des classes qui n'ont rien à voir avec mes itérateurs.

    Au niveau de la norme, aucun opérateur conversion implicite n'est défini dans la librairie standard.
    C'est ce qui me semblait, mais j'ai une eut une surprise (voir plus tard).

    Ils sont de toute manière dangereux, et moins maitrisable qu'un constructeur par copie (notamment lorsqu'on écrit des choses un peu compliquées).
    Pour un cas non_const -> const, si tu avais un exemple.
    Quel est l'avantage des constructeurs de copie ?
    Ne peuvent-ils pas jouer le même rôle (comme pour char*->std::string) ?

    Je le concède, c'est peu lisible, mais ça permet d'écrire :
    Ce qui est parfait. Est-ce mieux qu'un operateur de conversion ?


    Cependant, une chose me trouble : avec gcc 4.5, en -pedantic et -pedantic-errors, le code au dessus compile. Pourquoi ?

  12. #12
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par NoIdea Voir le message
    C'est pour sa qu'il faudrait alors que les typedefs soient traités plus tard.
    Quand les traiter alors ? Parce qu'il faut aussi qu'ils soient traités tôt, de manière a vérifier les équivalences de type.

    Citation Envoyé par NoIdea Voir le message
    Je ne suis pas d'accord : bien que l'on ne travaille pas sur la classe "extérieure", il peut être intéressant de limiter le type d'itérateurs :
    Dans mon exemple, ceci est très clair :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template<typename T>
    bool operator!=(const T& first, const T& second);
    Ceci compile. Mais quel risque : Le compilateur risque de me mettre qu'il ne sait qu'elle fonction choisir pour l'opérateur != pour des classes qui n'ont rien à voir avec mes itérateurs.
    C'est grosso-modo le code par défaut de l'opérateur !=. Et il a les même propriétés.

    Citation Envoyé par NoIdea Voir le message
    Pour un cas non_const -> const, si tu avais un exemple.
    J'en ai pas donné un ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    // création d'un itérateur non const
    std::vector<X>::iterator mutable_iterator = v.begin();
    // qui est "transformé" en itérateur const
    std::vector<X>::const_iterator constant_iterator = mutable_iterator;
    Citation Envoyé par NoIdea Voir le message
    Quel est l'avantage des constructeurs de copie ?
    Ne peuvent-ils pas jouer le même rôle (comme pour char*->std::string) ?
    Ce qui est parfait. Est-ce mieux qu'un operateur de conversion ?
    Un opérateur de conversion est appelée "tu ne sais pas quand". Par exemple, si std::string proposait un opérateur de conversion vers const char*, alors on pourrait écrire le code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    std::string s; // chaine vide
    if (s) // cast implicite en char* : le test est 
           // toujours vrai, car s.c_str() renvoie toujours un pointeur non null. 
    {
    ...
    }
    Puisque ce n'est certainement pas ce que veux faire l'utilisateur, autant l'interdire.

    Par le jeu des conversions, un cast implicite (qui appellerait l'opérateur de conversion) peut avoir des effets de bords difficiles à cerner. Les conditions qui font qu'ils seront utilisés sont souvent trop complexes pour qu'on puisse les traiter de manière systématique en programmant. Le code va alors se comporter au petit bonheur la chance, et advienne que pourra.

    (et j'ai dit une bêtise : le standard définit certains opérateurs de conversion pour certaines classes, notamment les streams, histoire de pouvoir écrire
    Ceci dit, il pose certaines limites sur leur comportement - pour éviter les conversions implicites suivant des conversions implicites).

    Un constructeur spécifique n'a pas ce genre de problème : on maitrise la conversion à l'entrée, pas en sortie. Les constructeurs permettent de choisir quelles conversions sont valides, alors que les opérateurs de conversions ne permettent pas d'avoir ce niveau de contrôle.

    Citation Envoyé par NoIdea Voir le message
    Cependant, une chose me trouble : avec gcc 4.5, en -pedantic et -pedantic-errors, le code au dessus compile. Pourquoi ?
    Euh... Quel code ?
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  13. #13
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Quand les traiter alors ? Parce qu'il faut aussi qu'ils soient traités tôt, de manière a vérifier les équivalences de type.
    A plusieurs reprises...

    C'est grosso-modo le code par défaut de l'opérateur !=. Et il a les même propriétés.
    J'ai même pas mis le code à l'intérieur (qui ne serait valide que pour mes itérateurs)!

    J'en ai pas donné un ?
    De cas où un operateur de conversion implicite était foireux, non.
    En gros ce que je voulais dire : dans quel cas un operateur de conversion implicite non_const->const est-il nocif ?

    Un opérateur de conversion est appelée "tu ne sais pas quand".
    J'ai envie de dire pareil pour les constructeur si on ne met pas explicit :
    char*-> std::string sans qu'on le sache.

    Par le jeu des conversions, un cast implicite (qui appellerait l'opérateur de conversion) peut avoir des effets de bords difficiles à cerner.
    A quelle condition ? Je ne voit pas où les effets de bords rentrent en jeu (mais je suis novice dans certain domaine du c++).

    Ceci dit, il pose certaines limites sur leur comportement - pour éviter les conversions implicites suivant des conversions implicites).
    Comment pose-t-il des limites ? N'est-on pas bloqué à une conversion implicite en c++ ?

    Un constructeur spécifique n'a pas ce genre de problème : on maitrise la conversion à l'entrée, pas en sortie. Les constructeurs permettent de choisir quelles conversions sont valides, alors que les opérateurs de conversions ne permettent pas d'avoir ce niveau de contrôle.
    Toujours eut l'impression (vraisemblablement à tord) qu'il n'y avait pas beaucoup de différence entre :

    Object(const Chose& );
    et
    Chose::operator Object()const;

    Euh... Quel code ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // création d'un itérateur non const
    std::vector<X>::iterator mutable_iterator = v.begin();
    // qui est "transformé" en itérateur const
    std::vector<X>::const_iterator constant_iterator = mutable_iterator;

  14. #14
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par NoIdea Voir le message
    A plusieurs reprises...
    Le compilateur a quand même un impératif de performance ; déjà que c'est très lent...

    Citation Envoyé par NoIdea Voir le message
    J'ai même pas mis le code à l'intérieur (qui ne serait valide que pour mes itérateurs)!
    Je me suis mal exprimé : c'est déjà la pseudo-signature de l'opérateur global.

    Citation Envoyé par NoIdea Voir le message
    De cas où un operateur de conversion implicite était foireux, non.
    En gros ce que je voulais dire : dans quel cas un operateur de conversion implicite non_const->const est-il nocif ?
    Si tu passes en const alors que tu pensais rester en non-const, ton comportement n'est plus le même ; le résultat peut être complètement silencieux si tu as prévu les fonctions correctes. Donc rendez-vous au runtime

    Cf. http://www.devx.com/cplus/10MinuteSolution/32145/1954

    Citation Envoyé par NoIdea Voir le message
    J'ai envie de dire pareil pour les constructeur si on ne met pas explicit :
    char*-> std::string sans qu'on le sache.
    Sauf que dans ce sens là, si l'appel du constructeur est implicite, alors il a du sens.

    Citation Envoyé par NoIdea Voir le message
    A quelle condition ? Je ne voit pas où les effets de bords rentrent en jeu (mais je suis novice dans certain domaine du c++).
    ...
    Citation Envoyé par NoIdea Voir le message
    Comment pose-t-il des limites ? N'est-on pas bloqué à une conversion implicite en c++ ?
    En définissant des opérateur des conversions implicite vers des types qui peuvent être convertis en bool et rien d'autre (par exemple en renvoyant un pointeur vers une méthode), au lieu de renvoyer par exemple un bool qui pourrait se retrouver automatiquement converti vers un autre type. Il y a un nombre maximum de conversion implicite que peux décider de faire le compilateur (deux si ma mémoire est bonne). Du coup, le standard se protège comme il peut. (cf le même article http://www.devx.com/cplus/10MinuteSolution/32145/1954)

    Dans le cas que j'ai donné ci-dessus (une classe chaîne qui renverrait un const char* de manière implicite grâce à l'opérateur de conversion), est-tu capable de dire (en lisant le code) que fait ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    void f(bool condition_result)
    { ... }
     
    void f(const void* param)
    { ... }
     
    void x()
    {
      ma_chaine s;
      f(s);
    }
    Les effets de bord peuvent être nombreux et dévastateurs. Si on s'en rend compte au moment de la compilation, tant mieux. Sinon, advienne que pourra (et vive les pénalités de retard sur un projet !)

    Citation Envoyé par NoIdea Voir le message
    Toujours eut l'impression (vraisemblablement à tord) qu'il n'y avait pas beaucoup de différence entre :

    Object(const Chose& );
    et
    Chose::operator Object()const;
    Il y a des différences subtiles qui font toute la différence Tout est une histoire de contrôle : qu'est ce que tu contrôle dans un cas que tu ne contrôle pas dans l'autre ? Qu'est-ce qui peux t'échapper ?

    Citation Envoyé par NoIdea Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // création d'un itérateur non const
    std::vector<X>::iterator mutable_iterator = v.begin();
    // qui est "transformé" en itérateur const
    std::vector<X>::const_iterator constant_iterator = mutable_iterator;
    Et bien tout simplement parce que les const_iterator ont un constructeur qui prends en paramètre un const iterator&

    Du coup, le ce n'est pas le mutable_iterator qui se transforme en const_iterator, mais le const_iterator qui se construit à partir du mutable_iterator.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  15. #15
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Il est possible de concevoir un système de type permettant ce que tu veux. Ce n'est pas le cas de celui du C++ et le modifier pour ce faire n'est pas envisagé (et vraisemblablement les conséquences seraient inacceptables; les "il faudrait" sans vérifier tous les détails, je m'en méfie; déjà que l'expérience montre que quand on pense avoir fait le tour en profondeur de modifications moins importantes que ça, on se trompe bien souvent).
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  16. #16
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Merci pour ces clarifications. Juste une dernière question : si const_iterator et iterator ont exactement les même fonctions, un opérator de conversion implicite est-il gênant ?
    Ensuite, je me demande si parfois les typedefs ne sont pas dangereux :

    si je fais typedef const Type* const_iterator, n'ai-je pas un risque que if(it) compile (alors que l'utilisateur pourrait penser que if(it) vérifie si it est valide) ?

    Cependant, les typedefs permettent une classe plus lisible et peut être une réduction de la taille du binaire.

    Une dernière question, si je décide d'implémenter les iterator en nested_class, peut-on faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct iterator : private Type*//private est là pour empêcher la conversion en bool.
    {
    //...
    };
    Ce qui éviterais de redéfinir l'opérateur =, +, +=, - , -=, !=, ==, ->, *, ...

  17. #17
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    En définissant des opérateur des conversions implicite vers des types qui peuvent être convertis en bool et rien d'autre (par exemple en renvoyant un pointeur vers une méthode), au lieu de renvoyer par exemple un bool qui pourrait se retrouver automatiquement converti vers un autre type. Il y a un nombre maximum de conversion implicite que peux décider de faire le compilateur (deux si ma mémoire est bonne). Du coup, le standard se protège comme il peut. (cf le même article http://www.devx.com/cplus/10MinuteSolution/32145/1954)
    Pour info, il sera possible en C++1x de définir un opérateur de conversion comme explicit, ce qui évitera tous les désagréments liés à ces techniques, et permettra tout de même d'écrire du code plus... explicitement.
    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.

  18. #18
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Pour info, il sera possible en C++1x de définir un opérateur de conversion comme explicit, ce qui évitera tous les désagréments liés à ces techniques, et permettra tout de même d'écrire du code plus... explicitement.
    Ce qui sera un bonne chose. Je crois
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

Discussions similaires

  1. Surcharge d'opérateur dans une classe template
    Par Opérateur dans le forum Langage
    Réponses: 6
    Dernier message: 22/12/2008, 03h26
  2. Opérateur template "undefined"
    Par Ch@hine dans le forum Langage
    Réponses: 6
    Dernier message: 20/04/2008, 18h19
  3. Opérateur new[] et template
    Par progfou dans le forum Langage
    Réponses: 29
    Dernier message: 08/03/2007, 11h31
  4. Réponses: 13
    Dernier message: 29/09/2006, 16h10
  5. Opérateur template ?
    Par Médinoc dans le forum Langage
    Réponses: 6
    Dernier message: 09/03/2006, 12h03

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