web-dev-qa-db-fra.com

Contraintes de modèle C ++

En C #, nous pouvons définir un type générique qui impose des contraintes sur les types qui peuvent être utilisés comme paramètre générique. L'exemple suivant illustre l'utilisation de contraintes génériques:

interface IFoo
{
}


class Foo<T> where T : IFoo
{
}

class Bar : IFoo
{
}

class Simpson
{
}

class Program
{
    static void Main(string[] args)
    {
        Foo<Bar> a = new Foo<Bar>();
        Foo<Simpson> b = new Foo<Simpson>(); // error CS0309
    }
}

Existe-t-il un moyen d'imposer des contraintes pour les paramètres de modèle en C++.


C++ 0x a un support natif pour cela, mais je parle de C++ standard actuel.

59
Jorge Ferreira

Comme quelqu'un d'autre l'a mentionné, C++ 0x intègre cela dans le langage. D'ici là, je recommanderais Bjarne Stroustrup 's suggestions pour les contraintes de modèle .

Edit: Boost a également une alternative de son propre .

Edit2: ressemble à les concepts ont été supprimés de C++ 0x .

34
luke

Si vous utilisez C++ 11, vous pouvez utiliser static_assert avec std::is_base_of dans ce but.

Par exemple,

#include <type_traits>

template<typename T>
class YourClass {

    YourClass() {
        // Compile-time check
        static_assert(std::is_base_of<BaseClass, T>::value, "type parameter of this class must derive from BaseClass");

        // ...
    }
}
53
Venemo

"Implicitement" est la bonne réponse. Les modèles créent efficacement un scénario de "typage du canard", en raison de la façon dont ils sont compilés. Vous pouvez appeler toutes les fonctions de votre choix sur une valeur de type modèle, et les seules instanciations qui seront acceptées sont celles pour lesquelles cette méthode est définie. Par exemple:

template <class T>
int compute_length(T *value)
{
    return value->length();
}

Nous pouvons appeler cette méthode sur un pointeur vers n'importe quel type qui déclare la méthode length() pour retourner un int. Ainsi:

string s = "test";
vector<int> vec;
int i = 0;

compute_length(&s);
compute_length(&vec);

... mais pas sur un pointeur vers un type qui ne pas déclare length():

compute_length(&i);

Ce troisième exemple ne sera pas compilé.

Cela fonctionne car C++ compile une nouvelle version de la fonction (ou classe) modèle pour chaque instanciation. En effectuant cette compilation, il effectue une substitution directe, presque macro, de l'instanciation du modèle dans le code avant la vérification de type. Si tout fonctionne toujours avec ce modèle, la compilation se poursuit et nous arrivons finalement à un résultat. Si quelque chose échoue (comme int* Ne déclarant pas length()), alors nous obtenons l'erreur redoutée de compilation du modèle de six pages.

36
Daniel Spiewak

Vous pouvez mettre un type de garde sur IFoo qui ne fait rien, assurez-vous qu'il est là sur T dans Foo:

class IFoo
{
public:
    typedef int IsDerivedFromIFoo;
};

template <typename T>
class Foo<T>
{
    typedef typename T::IsDerivedFromIFoo IFooGuard;
}
14
Eclipse

Découvrez Boost

Bibliothèque Boost Concept Check (BCCL)

La bibliothèque Concept Check permet d'ajouter une déclaration explicite et de vérifier concepts dans le style de extension de langage C++ proposée .

8
Nemanja Trifunovic

Sorte de. Si vous effectuez une diffusion statique sur un IFoo *, il sera impossible d'instancier le modèle à moins que l'appelant ne passe une classe qui peut être affectée à un IFoo *.

2
Lou Franco

Implicitement seulement.
Toute méthode que vous utilisez dans une méthode réellement appelée est imposée au paramètre de modèle.

1
shoosh

Tu peux le faire. Créez le modèle de base. Faites en sorte qu'il n'ait que des constructeurs privés. Créez ensuite des spécialisations pour chaque cas que vous souhaitez autoriser (ou faites le contraire si la liste interdite est beaucoup plus petite que la liste autorisée).

Le compilateur ne vous permettra pas d'instancier les modèles qui utilisent la version avec des constructeurs privés.

Cet exemple autorise uniquement l'instanciation avec int et float.

template<class t> class FOO { private: FOO(){}};

template<> class FOO<int>{public: FOO(){}};

template<> class FOO<float>{public: FOO(){}};

Ce n'est pas une manière courte et élégante de le faire, mais c'est possible.

0
OldMan