Question

En C #, nous pouvons définir un type générique qui impose des contraintes sur les types pouvant être utilisés en tant que 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 du C ++ standard actuel.

Était-ce utile?

La solution

Comme quelqu'un l'a mentionné, C ++ 0x est en train de l'intégrer au langage. Jusque-là, je recommanderais les Bjarne Stroustrup suggestions de contraintes de modèle .

Modifier: Boost a également un alternative différente .

Modifier2: Les concepts ont été supprimés de C ++ 0x. .

Autres conseils

Si vous utilisez C ++ 11, vous pouvez utiliser static_assert avec std :: is_base_of à cette fin.

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");

        // ...
    }
}

" implicitement " est la bonne réponse. Les modèles créent efficacement une "saisie de canard". scénario, en raison de la façon dont ils sont compilés. Vous pouvez appeler les fonctions de votre choix sur une valeur de type template et les seules instanciations 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 sur tout type déclarant la méthode length () pour renvoyer un int . Ainsi:

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

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

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

compute_length(&i);

Ce troisième exemple ne compilera pas.

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

Vous pouvez mettre un type de garde sur IFoo qui ne fait rien, assurez-vous qu'il soit présent sur T dans Foo:

class IFoo
{
public:
    typedef int IsDerivedFromIFoo;
};

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

Découvrez Boost

.
  

La bibliothèque de contrôle de concept Boost (BCCL)

     

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

En quelque sorte. Si vous static_cast sur un IFoo *, il sera impossible d'instancier le modèle à moins que l'appelant ne transmette une classe pouvant être affectée à un IFoo *.

Seulement implicitement.
Toute méthode que vous utilisez dans une méthode réellement appelée est imposée au paramètre template.

Vous pouvez le faire. Créez le modèle de base. Faites-le avoir que des constructeurs privés. Créez ensuite des spécialisations pour chaque cas que vous souhaitez autoriser (ou indiquez l'inverse si la liste des interdits est beaucoup plus petite que celle autorisée).

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

Cet exemple n'autorise l'instanciation qu'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 façon courte et élégante de le faire, mais c'est possible.

Regardez le modèle CRTP (modèle curieusement récursif). Il est conçu pour aider à prendre en charge l'héritage statique.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top