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++/CLI Discussion :

contraindre le type d'un template class T


Sujet :

C++/CLI

  1. #1
    Membre régulier
    contraindre le type d'un template class T
    Bonjour à tous,

    J'aimerais savoir si il est possible de contraindre un template à être d'une certaine origine.

    En gros :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    template<class T>
    class Data {
    protected:
    	T* previousData;
    	T* actualData;
    public:
    ...
    };
     
    template<class U>
    class BackUpData:public Data<U>{
    ...
    };


    J'aimerais dire au compilateur que la classe U doit être forcément une classe fille d'une classe V.

    Comment faire?

  2. #2
    Membre régulier
    euh non ce n'est pas possible à mon sens.

    pourquoi ne pas enlever ton T pour mettre tout simplement la classe mere?

    apres tu definis la classe mère comme étant non instanciable. (abstract) et le tour est joué.
    dev delphi | c# .Net - .Net CF - réseau - silverlight
    Motard a ses heures

  3. #3
    Membre régulier
    La classe mère est Data. Je veux pouvoir utiliser la structure pour ensuite mettre T en float ou avec un autre objet.

  4. #4
    Membre régulier
    je dois pas tout comprendre dans le but de ta demarche
    dev delphi | c# .Net - .Net CF - réseau - silverlight
    Motard a ses heures

  5. #5
    Inactif  
    Citation Envoyé par ludo86 Voir le message
    J'aimerais dire au compilateur que la classe U doit être forcément une classe fille d'une classe V.

    Comment faire?

    En C#, il existe le mot clef where qui sert précisément à cela. Je pense qu'il doit exister un équivalent C++ mais lequel ?

    Je ne réponds pas aux questions techniques par MP ! Le forum est là pour ça...


    Une réponse vous a aidé ? utiliser le bouton

    "L’ennui dans ce monde, c’est que les idiots sont sûrs d’eux et les gens sensés pleins de doutes". B. Russel

  6. #6
    Rédacteur/Modérateur

    C'est je crois possible de faire ça, bien qu'un peu tordu, à base de static_assert et de type_traits, mais je questionnerait avant tout l'intérêt de la chose.

    En C#, ça peut avoir du sens, mais en C++, je trouve que ça contraint souvent bien trop fortement les arguments templates. Pourquoi vouloir empêcher une autre classe qui possède toute l'interface requise d'être utilisée en tant que T ?
    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.

  7. #7
    Rédacteur/Modérateur

    Citation Envoyé par JolyLoic Voir le message
    En C#, ça peut avoir du sens, mais en C++, je trouve que ça contraint souvent bien trop fortement les arguments templates. Pourquoi vouloir empêcher une autre classe qui possède toute l'interface requise d'être utilisée en tant que T ?
    En fait, le fait qu'il y ait des contraintes sur les paramètres de type générique en C# et pas en C++ s'explique par la différence de fonctionnement entre les génériques de C# et les templates de C++... Ca semble similaire, mais en fait ça ne fonctionne pas du tout de la même manière.

    En C++, le compilateur instancie le template avec les arguments de type qu'on lui fournit, et il vérifie que ça colle (par exemple si dans le template on appelle une méthode Toto, le compilo vérifie que cette méthode existe bien dans la classe passée en argument). Pas besoin de dire que l'argument du template doit hériter d'une classe donnée, s'il a les membres qui vont bien, ça passe. C'est une forme de "duck typing" en gros...

    En C#, un type générique est compilé indépendamment des types qu'on pourrait lui passer en paramètre : c'est un type à part entière, pas juste un "modèle" pour créer d'autres types. Donc si on a besoin d'accéder à un membre d'un paramètre de type, il faut mettre des contraintes pour donner des informations plus spécifiques sur ce type. Par exemple ce code :

    Code C# :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    static class Foo<T>
    {
        public static int Bar(T obj)
        {
            return obj.Count;
        }
    }


    ne compilera pas, parce que le compilateur ne peut pas vérifier que le type T a une propriété Count.

    Si on rajoute une contrainte :
    Code C# :Sélectionner tout -Visualiser dans une fenêtre à part
    static class Foo<T> where T : ICollection


    Le compilateur sait que T implémente ICollection, et qu'il a donc une propriété Count. Et là ça passe...


    Pour répondre à la question d'origine : si le template appelle des méthodes de U qui sont déclarées dans V, ça ne compilera que si la classe U a effectivement ces méthodes. Donc si U hérite de V, ça passera. Mais il est aussi possible que ça passe sans que U hérite de V, à condition que U expose bien les membres nécessaires...

    Bon, après je suis pas du tout expert en C++, donc je dis peut-être des bêtises... mais il me semble que c'est à peu près comme ça que ça se passe.

  8. #8
    Candidat au Club
    Bonjour,

    Je ne sais pas si ça peu aider ....
    Je n'ai que très peu crée de template en c++ mais il est possible de restreindre un comportement de template via une spécialisation totale ou partielle + opérateur de conversion pour autoriser certaines conversions d'un type dérivée vers un type de base:

    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
    class C_A {
    // ...
    };
     
    class C_B : public C_A {
    // ...
    };
     
    template <class T> class MyTemplate {
    private: 
        T* p;
    public:
        MyTemplate(T* ip) : p(ip) {}
     
        template <class U> operator MyTemplate<U> () ;
    // ...
    };
     
    template <class T> template <class U> MyTemplate<T>::operator MyTemplate<T2> () { /*conversion d'un MyTemplate<T> vers MyTemplate<U> à  définir */ }
     
    //...
    C_B cb;
    MyTemplate<C_B> ptrc = &cb;
     
    // conversion MyTemplate<C_B> -> MyTemplate<C_A>
    MyTemplate<C_A> ptrs = ptrc; // fait appel à template <class U> operator MyTemplate<U> () ;


    De ce fait, tu pourrais interdire tout autre forme de conversion autre que celles que tu as prévue dans template <class U> operator MyTemplate<U> () ;.

    Bonne journée

  9. #9
    Expert confirmé
    Bonjour,

    La conception m'a l'air un peu étrange mais, pour répondre à la question :
    Citation Envoyé par ludo86 Voir le message
    J'aimerais dire au compilateur que la classe U doit être forcément une classe fille d'une classe V.

    Comment faire?
    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
    #include <type_traits>
     
    template<class T>
    class Data {
    protected:
    	T* previousData;
    	T* actualData;
    };
     
    class V {};
     
    template<class U>
    class BackUpData : public Data<U> {
    	static_assert(std::is_base_of_v<V, U>);
    };
     
    // Tests:
     
    class ChildOfV : public V {};
    class NotChildOfV {};
     
    // Explicit template instantiations:
    template class BackUpData<ChildOfV>; // does compile
    //template class BackUpData<NotChildOfV>; // does not compile