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 :

Template : aider le compilateur à inférer un type


Sujet :

C++

  1. #1
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut Template : aider le compilateur à inférer un type
    Comme on peut s'en douter, le code ci-dessous ne compile pas (GCC 6.2.1 mais peu importe je pense) : le compilateur ne parvient pas à inférer le premier paramètre T. En précisant float ligne 17, ça passe crème.

    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
    // normalizes an interpolation parameter prior to forwarding it
    template <typename T, T (*Func)(const T&)>
    inline
    T normalizeParams(const T& t, const T& start, const T& end) {
        const T range(end - start);
        return start + Func((t - start) / range) * range;
    }
     
    // expects a normalized parameter
    template <typename T>
    inline
    T easeIn(const T& t) {
        return t * t * t;
    }
     
    int main() {
        float a(normalizeParams</*float, */easeIn>(2.24f, .846f, 3.451f));
        return int(a);
    }
    Oui mais bon, avoir à préciser T n'est pas satisfaisant. Or Func en dépend. Y aurait-il un moyen de donner un coup de pouce au compilo ?

  2. #2
    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
    À moins de mettre Func en tant que paramètre de fonction, non.

    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
    template <typename T, class Func = T (*)(const T&)>
    inline
    // pas obliger de mettre Func à la fin
    T normalizeParams(const T& t, const T& start, const T& end, Func && f) {
        const T range(end - start);    
        return start + f((t - start) / range) * range;
    }
     
    // expects a normalized parameter
    template <typename T> 
    inline
    T easeIn(const T& t) {
        return t * t * t; 
    }
     
    int main() {
        float a(normalizeParams(2.24f, .846f, 3.451f, easeIn));
        // fonctionne aussi avec des types plus complexes
        float b(normalizeParams(2.24f, .846f, 3.451f, [](auto v){ return v; }));
        return int(a) + int(b);
    }

  3. #3
    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
    De la même manière que les paramètres par défaut doivent être à droite et non à gauche, tu ne peux pas ne pas écrire un paramètre template à gauche et écrire celui à droite.
    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.

  4. #4
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Citation Envoyé par jo_link_noir Voir le message
    À moins de mettre Func en tant que paramètre de fonction, non.


    Merci pour ta proposition. Pour quelle raison déclares-tu f en tant que rvalue reference ?


    Citation Envoyé par Bousk Voir le message
    De la même manière que les paramètres par défaut doivent être à droite et non à gauche, tu ne peux pas ne pas écrire un paramètre template à gauche et écrire celui à droite.
    J'ai bien compris que je ne peux inverser les paramètres du template et pourquoi cette construction est rejetée. Je cherchais à savoir s'il était possible de conserver la fonctionnalité sans avoir à préciser T. Au prix d'une refactorisation, bien entendu.

  5. #5
    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
    Ton problème, si je vois bien, c'est que easeIn n'est pas une fonction, mais une template.
    Du coup tu as besoin de spécifier un parametre de template qui soit lui même une template. On parle de template template parameter.

    Tu as template <typename T> T easeIn(const T& t);. une template de fonction T(T const&) nommée easeIn.

    tu veux pouvoir écrire float a(normalizeParams<easeIn>(2.24f, .846f, 3.451f));, ou normalize est une template de fonction d'un type vers un autre.

    La déclaration serait proche de:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    template <template <typename> class Func, typename T>
    typename std::enable_if< un truc magique qui dit que Funct est compatible avec T func(T) , T>::type
    normalizeParams(const T& t, const T& start, const T& end);
    cf template template parameter et template argument deduction sur cppreference.com
    Et encore, je ne suis pas certain que ca fonctionne, car j'ai l'impression que ca ne peut fonctionner qu'avec des classes.

    Sinon, tu peux aussi prendre la fonction en argument normal. De toute façon, tu paies déjà le coût d'un pointeur de fonction.
    Mais tu devras spécifier easeIn<Float>.

    Personnellement, je passerai par une fonction intermédiaire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typename T>
    inline T normalizeParams_easeIn(T const& t, T const& start, T const& end) {
        return normalizeParams<T, easeIn<T>>(t, start, end);
    }

  6. #6
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par défaut
    La solution de jo_link_noir est la bonne. Utiliser un pointeur en paramètre template (de fonction ou non) est une mauvaise idée car il y a des contraintes en terme d'external linkage sur les pointeurs utilisés, et tu te prives des callables autre que des fonctions libres. L'avantage est que tu peux passer n'importe quel callable, que ce soit une fonction libre, un foncteur, une lambda, un std::function, une bind expression. Il n'y a même pas besoin de préciser une valeur par défault.

    La syntaxe && dans ce cas là n'est pas une rvalue référence car c'est un paramètre template déduit. Voir ce sujet.

  7. #7
    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 Bousk Voir le message
    [...] tu ne peux pas ne pas écrire un paramètre template à gauche et écrire celui à droite.
    Si, comme pour std::make_array. Par contre, un paramètre ne peut pas dépendre d'un autre situé à sa droite.
    À savoir aussi que l'ordre en paramètre de fonction n'a pas besoin de suivre celui de la template. Dans mon exemple, même si Func dépend de T, la variable de type Func peut être en premier paramètre.

    Citation Envoyé par Matt_Houston Voir le message
    Merci pour ta proposition. Pour quelle raison déclares-tu f en tant que rvalue reference ?
    Pas de raison particulière, c'est pour éviter les copies éventuellement coûteuse, mais une référence constante fait bien le boulot aussi. À condition que l'opérator() soit lui même constant.


    @leternel: Les fonctions sont hypers limitatif et ne sont pas considérées comme des types templates puisqu'une fonction est avant tous une valeur pour la quel on déduit un type. À partir du type, on a une signature de fonction et non un type unique représentant la fonction. Du coup, pas possible de faire signature -> template et pas possible d'avoir une fonction template en tant que paramètre template puisqu'il n'y a pas d'identifiant unique (contrairement aux classes).

  8. #8
    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
    C'est bien ce qu'il me semblait.

    Par contre, dans ta solution, easeIn n'étant pas une fonction (puisque c'est une template), on dois préciser easeIn<float>, non?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    float a(normalizeParams(2.24f, .846f, 3.451f, easeIn<float>));
    Et du coup, tu perds l'intérêt de la chose, à savoir ne pas préciser float.
    Peut-être transformer easeIn en foncteur générique (ce que fait la lambda [](auto a) {...})
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    struct easeIn {
        template <typename T> T operator() (T const& a) const {return a*a*a;}
    };

  9. #9
    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
    Citation Envoyé par jo_link_noir Voir le message
    Si, comme pour std::make_array.
    je ne vois rien dans ton lien qui montre qu'on écrit les arguments template de droite et non de gauche.
    Il y a juste utilisation de variadic template et un void par défaut pour le packing et dans le cas où on utiliserait un truc vide de ce que je vois/comprends avec l'utilisation interne de std::common_type (et SFINAE ?).
    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.

  10. #10
    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 leternel Voir le message
    Par contre, dans ta solution, easeIn n'étant pas une fonction (puisque c'est une template), on dois préciser easeIn<float>, non?
    Non. Comme que le compilateur ne sait pas déduire le type, il prend la valeur par défaut de Func (pointeur sur fonction) et fait un cast implicite. Dans ce contexte, static_cast<float(*)(float const &)>(easeIn) et l'équivalent de easeIn<float>.
    Dans la stl, il y a std::endl et les autres manipulateurs qui fonctionnent sur le même principe.


    @Bousk: Visiblement je n'ai pas compris, j'avais l'impression que tu disais qu'un paramètre par défaut ne pas se situer à gauche.

  11. #11
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Merci pour vos contributions, j'ai opté pour la forme proposée par jo_link_noir. Je passe le fil en résolu (sans clore le débat).


    Citation Envoyé par jblecanard Voir le message
    La syntaxe && dans ce cas là n'est pas une rvalue référence car c'est un paramètre template déduit. Voir ce sujet.
    Je ne connaissais pas cette interprétation. C'est conséquent, je vais digérer ça au calme.

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 30/07/2013, 21h01
  2. [template] Instanciation d'une liste de types pour un plugin
    Par Matthieu Brucher dans le forum C++
    Réponses: 6
    Dernier message: 11/01/2007, 07h54
  3. Réponses: 5
    Dernier message: 14/12/2005, 13h02
  4. Réponses: 4
    Dernier message: 31/03/2005, 17h55

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