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 :

Instanciation d'un template qui devrait être invalide, mais ça passe


Sujet :

Langage C++

  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut Instanciation d'un template qui devrait être invalide, mais ça passe
    Bonjour,

    Voici un code :
    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
    #include <iostream>
     
    template<typename T>
    class Wrapper {
    public:
    	Wrapper(T value) :
    			raw_data(value) {
    	}
     
    	explicit operator T() const {
    		return raw_data;
    	}
     
    	friend const Wrapper operator *(const Wrapper& multiplier, const Wrapper& multiplicand) {
    		T raw_result = multiplier.raw_data * multiplicand.raw_data;
    		return Wrapper(raw_result);
    	}
     
    private:
    	T raw_data;
    };
     
    using MyWrapper = Wrapper<int>;
     
    template<typename U>
    int multiply_then_convert(U value) {
    	U multiplied = 2 * value;
    	int converted = multiplied; // WTF? explicit?
    	return converted;
    }
     
    int main() {
    	{
    		// Avec fonction template
    		MyWrapper value = 25;
    		int converted = multiply_then_convert(value);
    	}
     
    	{
    		// Equivalent
    		MyWrapper value = 25;
    		MyWrapper multiplied = 2 * value;
    		int converted = multiplied;
    	}
    }
    La ligne 43 ne compile pas. C'est normal, l'opérateur de conversion est explicite, il faut donc faire un cast.

    La ligne 28 fait la même chose. L'instanciation de cette fonction template pour un tel U (= Wrapper<int>) ne devrait donc pas compiler. Or, la ligne 36 passe comme une lettre à la Poste. L'exécution produit le résultat attendu (50).

    Pourquoi ?

    Merci d'avance pour vos réponses !

  2. #2
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    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 202
    Par défaut
    Je confirme qu'avec mon gcc 7, j'ai le même résultat.

    Et la même chose avec la variante suivante de ta template.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template<typename U>
    int multiply_then_convert(Wrapper<U> value) {
    	Wrapper<U> multiplied = 2 * value;
    	int converted = multiplied; // WTF? explicit?
    	return converted;
    }
    Je ne comprends pas pourquoi, cependant.

  3. #3
    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,

    La raison est toute simple: c'est toujours le premier paramètre qui prime pour les opérateurs...

    Ainsi, quand tu écris un code proche de MyWrapper multiplied = 2 * value; //où value est de type MyWrapper, si tu laisses (temporairement) la partie gauche de l'affectation de coté, tu te retrouve avec 2*value; et, comme 2 est un littéral, le compilateur pourra choisir entre
    • int operator * (int, int)
    • unsigned int operator * (unsigned int , unsigned int)
    • long operator * (long, long)
    • unsigned long operator * (unsigned long, unsigned long)

    (le tout dépendant du type qu'il va associer à 2 )

    Mais il ne pensera jamais à tenter une conversion dans une optique SFINAE...

    Alors, tu me demanderas sans doute pourquoi MyWrapper = value * 2; //où value est de type MyWrapper pourrait fonctionner

    Hé bien, encore une fois, parce que le premier paramètre prime, et que, vu qu'il a un wrapper<int> comme premier paramètre, il va "logiquement" penser à faire la conversion de 2 en une instance (anonyme temporaire) de Wrapper<int>, vu qu'il saura que c'est... en cela qu'il doit faire la conversion

    Si tu veux que le premier code fonctionne, tu devras fournir un opérateur * dont le premier paramètre puisse être un littéral, sous une forme qui pourrait très bien être proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typename T>
    Wrapper<T> operator * (T litteral, Wrapper<T> const & w){
        return w * litteral; // on utilise l'opérateur qui fonctionne ;)
    }
    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

  4. #4
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    Bah moi j'ai une erreur avec gcc-7.2 et clang-5.

    @koala: ce n'est pas la multiplication qui cause problème, mais le cast implicite vers un int. Ton résonnement est faux aussi, pour les fonctions libres, ce n'est pas le premier paramètre qui prime mais tous les paramètres dans leur ensemble. Mais puisque la conversion implicite vers un int est théoriquement impossible, le prototype operator*(int,int) est exclus et operator*(Wrapper<int>,Wrapper<int>) est utilisé (appel implicite du constructeur de Wrapper).

  5. #5
    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 jo_link_noir Voir le message
    Ton résonnement est faux aussi, pour les fonctions libres, ce n'est pas le premier paramètre qui prime mais tous les paramètres dans leur ensemble.
    Pour les fonctions libres, oui, mais, ici, on est dans le cas spécifique d'un opérateur, qui ne prend pas deux paramètres quelconques, mais bien deux opérandes : un qui se trouve à gauche de l'opérateur, et l'autre qui se trouve à droite de l'opérateur.

    Or, dans ce cas particulier, c'est bel et bien l'opérande de gauche (et donc le premier paramètre) qui prime.La preuve, bien que les deux codes soient sensiblement identique, tu obtiendra un résultat différent avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int main(){
        int i = 3;
        float f= 3.141592;
        auto result = i * f;
        std::cout<<result<<"\n";
    }
    ou avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int main(){
        int i = 3;
        float f= 3.141592;
        auto result = f * i;
        std::cout<<result<<"\n";
    }
    Pourquoi parce que le compilateur effectue une conversion implicite du deuxième paramètre (de l'opérande de droite, pour être précis) pour qu'il corresponde au type du premier (de l'opérande de gauche, donc), et que le type du résultat dépend également de l'opérande de gauche

    Ici, on est exactement dans le même cas: si tu veux pouvoir profiter de la commutativité entre un type primitif et ton type défini par l'utilisateur, tu dois prévoir les deux situations, car le compilateur n'envisagera jamais la conversion de l'opérande de gauche

    EDIT : A la limite, tu prévoirais une fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typename T>
    Wrapper<T> multiply(Wrapper<T> const & a, Wrapper<T> const & b){
        /* ... */
    }
    là, effectivement, tu devrais pouvoir utiliser deux wrapper, un wrapper et un entier (dans n'importe quel ordre), voir même -- a priori -- deux entiers, car le compilateur envisagera la conversion implicite si elle lui est autorisée (ce qui est le cas ici).

    Mais, c'est valable pour les fonctions libres, pas pour les opérateurs
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Pour les fonctions libres, oui, mais, ici, on est dans le cas spécifique d'un opérateur, qui ne prend pas deux paramètres quelconques, mais bien deux opérandes : un qui se trouve à gauche de l'opérateur, et l'autre qui se trouve à droite de l'opérateur.
    Ce qui est identique à un appel de fonction libre, sauf au niveau de la syntaxe.

    Citation Envoyé par koala01 Voir le message
    Or, dans ce cas particulier, c'est bel et bien l'opérande de gauche (et donc le premier paramètre) qui prime.La preuve, bien que les deux codes soient sensiblement identique, tu obtiendra un résultat différent avec [...]
    Non, le résultat est identique. Sur les scalaires, les opérandes sont convertis vers le type pouvant prendre la plus grande valeur entre les 2 opérandes. Donc int+float ou float+int donne toujours un float car l'opérateur utilisé est operator+(float,float).

    Citation Envoyé par koala01 Voir le message
    EDIT : A la limite, tu prévoirais une fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typename T>
    Wrapper<T> multiply(Wrapper<T> const & a, Wrapper<T> const & b){
        /* ... */
    }
    là, effectivement, tu devrais pouvoir utiliser deux wrapper, un wrapper et un entier (dans n'importe quel ordre), voir même -- a priori -- deux entiers, car le compilateur envisagera la conversion implicite si elle lui est autorisée (ce qui est le cas ici).

    Mais, c'est valable pour les fonctions libres, pas pour les opérateurs
    Si. C'est ce qui d'ailleurs fait dans le code de @Bktero et cela fonctionne sans surprise. Sauf avec 2 entiers bien sûr.

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    J'avais mis un libre pour avoir un code plus représentatif de mon cas complet, je vois que ça brouille les cartes.

    Je l'ai enlevé et la situation demeure.

    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
    #include <iostream>
     
    template<typename T>
    class Wrapper {
    public:
    	Wrapper(T value) :
    			raw_data(value) {
    	}
     
    	explicit operator T() const {
    		return raw_data;
    	}
     
    private:
    	T raw_data;
    };
     
    using MyWrapper = Wrapper<int>;
     
    template<typename U>
    int convert(U value) {
    	int converted = value; // WTF? explicit?
    	return converted;
    }
     
    int main() {
    	{
    		// Avec fonction template
    		MyWrapper value = 25;
    		int converted = convert(value);
    	}
     
    	{
    		// Equivalent
    		MyWrapper value = 25;
    		int converted = value;
    	}
    }
    ..\main.cpp:30:7: warning: unused variable 'converted' [-Wunused-variable]
    ..\main.cpp:36:19: error: cannot convert 'MyWrapper {aka Wrapper<int>}' to 'int' in initialization
    ..\main.cpp:36:7: warning: unused variable 'converted' [-Wunused-variable]
    Je compile avec mingw :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $ gcc --version
    gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.2.0
    Copyright (C) 2017 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

  8. #8
    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
    En fait, je me suis mal exprimé...

    Si l'opérateur * (ou n'importe quel opérateur mathématique) était représenté sous la forme d'une fonction template, ce serait sous la forme "par défaut" de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <typename T>
    T operator * (T a, T b)
    et non sous une forme qui pourrait être proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <typename RT, typename Left, typename Rigth>
    RT operator(Left a, Rigth b)
    Ce qui fait que les types de retour et de l'opérande de droite dépendent du premier type qui pourra être déduit par le compilateur, à savoir... l'opérande de gauche.

    En cas de besoin, le compilateur acceptera une conversion implicite pour l'opérande de droite (par exemple, de float vers int), si tant est qu'elle soit possible, mais il n'essayera aucune conversion pour l'opérande de gauche, vu que c'est son "type de référence".

    C'est ce qui explique qu'un auto result = myWrapperValue * 3; fournira sera admis (parce qu'il existe un constructeur s'attendant à recevoir un int, qui permet la conversion implicite de 3 en Wrapper), mais que auto result = 3 * myWrapperValue; sera pas admis, parce que l'opérateur de conversion est défini comme étant explicite.

    Si on supprimait la définition explicite de l'opérateur de conversion, cette ligne pourrait être acceptée, mais result serait alors... de type int.

    Et encore, à la condition expresse que la spécialisation de myWrapperValue soit cohérente avec le type de la valeur littérale: si c'est Wrapper<int> (ou n'importe quel autre type entier), ca passera, mais si c'est Wrapper<float> (ou n'importe quel type réel), ca ne passera pas, car il y aurait alors deux conversions implicites à faire pour obtenir l'entier nécessaire comme deuxième paramètre: de Wrapper<float> vers float et de float vers int.

    On peut changer cela, en indiquant un type de retour ou un type pour l'opérande de droite différent de celui de l'opérande de gauche (et donc se retrouver dans la deuxième situation), mais on doit alors le faire de manière explicite.

    Et donc, si tu veux pouvoir profiter de la commutativité, et disposer d'un Wrapper<int> en résultat, tu dois bel et bien définir deux opérateurs *:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <typename T>
    Wrapper<T> operator * (Wrapper<T> const & a, Wrapper<T> const & b)
    qui fonctionnera aussi bien avec auto result = wrapperValue1 * wrapperValue2; qu'avec auto result = wrapperValue1 * 3; grâce à la conversion implicite autorisée par le constructeur de Wrapper, et...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <typename T>
    Wrapper<T> operator * (T a, Wrapper<T> const & b)
    qui permettra à l'opérande de gauche de ne pas être un wrapper.

    Alors, je me suis peut être mal exprimé dans mes interventions précédentes, j'ai peut être pris des raccourcis excessifs, soit, mais que l'on ne vienne pas me dire que j'ai tord, car la conclusion finale est bel et bien tout ce qu'il y a de plus correcte.
    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 Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    @Bktero*: Après avoir réessayé de compiler, j'ai bien qu'une erreur avec gcc, mais 2 avec clang. Sur le premier code aussi. Je me demande bien comment je m'y suis pris la première fois .
    J'ai regardé un peu dans le bugzilla de gcc mais n'est rien trouvé, il faudrait probablement ouvrir un rapport de bug.

    @Koala01: À moins de ne rien comprendre à ce que tu dis, je suis en total désaccord.

    Dans un prototype tel que T operator * (T a, T b), il n'y a pas de priorisation d'opérande. Le compilateur ne pourra pas déterminer le type de T si les 2 paramètres ne sont pas du même type. Dans le cas contraire, il y a ambiguïté dans la résolution de T.

    Par conséquent, un prototype de la forme Wrapper<T> operator*(Wrapper<T> const& a, Wrapper<T> const& b) fonction pour Wrapper<T> * T, Wrapper<T> * T ET Wrapper<T> * Wrapper<T>.
    Puisque que parmi les prototypes sélectionnable on trouve Wrapper<T> operator*(Wrapper<T> const& a, Wrapper<T> const& b) et T operator*(T const& a, T const& b), mais que le second se fait rejeter car Wrapper n'est pas convertible en T.

  10. #10
    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
    Tu as beau être en désaccord, il n'empêche que la triste réalité de l'implémentation me donne raison: comme les opérateurs mathétmatiques sont infixe, le premier type susceptible d'être déduit par le compilateur est l'opérande de gauche, et, quel que soit le type de l'opérande de droite, tu n'aura pas ambiguité s'il existe une conversion implicite de ce type vers le type de l'opérande de droite.

    Autrement, un code aussi simple que auto result = 3/* sans doute un int */ * 3.141592 /* sans doute un float*/; serait refusé par le compilateur. Or, il n'en est rien...
    Par conséquent, un prototype de la forme Wrapper<T> operator*(Wrapper<T> const& a, Wrapper<T> const& b) fonction pour Wrapper<T> * T, Wrapper<T> * T ET Wrapper<T> * Wrapper<T>.
    Je présume que c'est une simple coquille de ta part, et que tu voulais écrireun prototype de la forme Wrapper<T> operator*(Wrapper<T> const& a, Wrapper<T> const& b) fonction pour Wrapper<T> * T, T * Wrapper<T> ET Wrapper<T> * Wrapper<T>. Mais bon, on se sera compris .

    Pour toute fonction libre qui ne serait pas un opérateur mathématique, je ne pourrais pas faire autrement que d'être tout à fait d'accord avec toi. Et je le suis très sincèrement, car tu as tout à fait raison. Et je l'ai d'ailleurs clairement indiqué...

    Mais, qu'il s'agisse d'un bug commun à l'ensemble des compilateurs ou d'une clause "cachée" de la norme, le fait reste que la mise en pratique te contredit, et tend à accréditer le fait que le type de retour d'un opérateur ainsi que celui de ses deux opérandes dépend du type ... de l'opérande de gauche.

    Je peux me tromper sur la cause de ce problème, mais la conclusion et la solution que j'en tire n'en est pas moins juste: si tu n'as pas la possibilité de profiter d'une conversion implicite de l'opérande de droite pour qu'il puisse correspondre à celui de l'opérande de gauche, ou si tu veux un type de retour différent du type de l'opérande de gauche, tu dois prévoir une "spécialisation partielle" (ce n'est pas vraiment le terme adapté, soit) qui défini clairement un type différent soit comme type de retour, soit comme type de l'opérande de droite.

    [EDIT] Il se peut que nous soyons face à un bug des différents compilateurs testés; ce ne serait surement pas la première fois que différents compilateurs souffrent de problèmes similaire, n'est-ce pas

    Mais, si c'était le cas, nous en trouverions une trace quelque part, car je suis près à parier ma chemise sur le fait que Bktero n'est pas le premier à être confronté au problème.

    Dés lors, la solution la plus simple étant toujours la meilleure, on peut raisonnablement partir du principe qu'il s'agit effectivement d'une clause quelconque (peut-être n'est-ce juste qu'une note en bas de page) de la norme qui définit ce comportement tout particulier pour les opérateurs mathématiques
    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
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 147
    Billets dans le blog
    4
    Par défaut
    J'ai du mal à suivre toutes vos histoires, mais je suis d'accord dans l'ensemble sur le coup de la conversion implicite d'un ou plusieurs opérandes. Sauf que là le seul opérateur de conversion possible de Wrapper en int est explicite.
    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.

  12. #12
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    @koala01 : tu te méprends totalement sur la nature du problème ici. On se moque totalement des lignes multiplied = 2 * value; (où il n'y a aucun problème particulier soit-dit en passant hormis le const de trop sur le retour de l'opérateur), on s'intéresse à l'opérateur de conversion vers int marqué explicit et aux lignes 28 et 48 suivantes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int converted = multiplied;
    et surtout celle dans la fonction multiply_then_convert() marquée du commentaire // WTF? explicit?.

    Car oui en mettant en parallèle le code du main() et celui de la fonction :






















    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // Equivalent
    MyWrapper value = 25;
    MyWrapper multiplied = 2 * value;
    int converted = multiplied;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template<typename U>
    int multiply_then_convert(U value) {
    	U multiplied = 2 * value;
    	int converted = multiplied; // WTF? explicit?
    	return converted;
    }
    avec son appel :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    MyWrapper value = 25;
    int converted = multiply_then_convert(value);
    qui nous donne après déduction du paramètre template U :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int multiply_then_convert(MyWrapper value) {
    	MyWrapper multiplied = 2 * value;
    	int converted = multiplied; // WTF? explicit?
    	return converted;
    }
    nous avons exactement le même code, or nous obtenons avec le compilateur GCC :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    main.cpp:43:19: error: cannot convert 'MyWrapper {aka Wrapper<int>}' to 'int' in initialization
       int converted = multiplied;
                       ^~~~~~~~~~
    Aucune erreur ! Là où on se serait attendu à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    main.cpp:28:18: error: cannot convert 'MyWrapper {aka Wrapper<int>}' to 'int' in initialization
       int converted = multiplied; // WTF? explicit?
                       ^~~~~~~~~~

    Comme jo_link_noir je pense qu'il s'agit d'un bogue de GCC. Clang donne bien une erreur, de même que MSVC (une fois l'erreur du main commentée).
    Dernière modification par Invité ; 24/02/2018 à 18h57.

  13. #13
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Je confirme la clarification de WinJerome : l'opérateur mathématique est hors sujet. Je l'ai d'ailleurs enlevé dans mon 2e code en #7. La question est bien sûr la conversion explicite qui est utilisée comme une conversion implicite dans le template.

    Je viens de tester plusieurs compilateurs avec Compiler Explore (https://godbolt.org/g/a6TxQZ) et ça dépend des compilateurs. zapcc, clang et icc râlent ; gcc et msvc ne disent rien. J'opterai bien pour un bug de gcc.

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

Discussions similaires

  1. Réponses: 12
    Dernier message: 03/09/2015, 19h06
  2. Requête HTTP qui devrait être toute simple !
    Par fagma dans le forum GWT et Vaadin
    Réponses: 11
    Dernier message: 27/09/2012, 12h23
  3. hover(), l'objet qui devrait être visible reste caché
    Par moumous24 dans le forum jQuery
    Réponses: 1
    Dernier message: 25/01/2011, 21h26
  4. [PMD] règle PMD non violée, mais qui devrait l'être!
    Par lassto dans le forum Qualimétrie
    Réponses: 0
    Dernier message: 18/05/2010, 10h14
  5. Réponses: 6
    Dernier message: 20/07/2007, 13h16

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