85 votes

Restreindre les paramètres des modèles C++ aux sous-classes

Comment puis-je forcer un paramètre de modèle T être une sous-classe d'une classe spécifique Baseclass ? Quelque chose comme ça :

template <class T : Baseclass> void function(){
    T *object = new T();

}

3 votes

Qu'essayez-vous d'accomplir en faisant cela ?

6 votes

Au contraire, c'est très pertinent. Elle détermine si c'est une bonne idée ou non de mettre du travail dans ce test. Dans de nombreux cas (tous ?), il n'est absolument pas nécessaire d'appliquer ces contraintes vous-même, mais plutôt de laisser le compilateur le faire lors de l'instanciation. Par exemple, pour la réponse acceptée, il serait bon de mettre une vérification pour savoir si T dérivé de Baseclass . Pour l'instant, cette vérification est implicite, et n'est pas visible pour la résolution des surcharges. Mais si une telle contrainte implicite n'est faite nulle part, il semble n'y avoir aucune raison pour une restriction artificielle.

1 votes

Oui, je suis d'accord. Cependant, je voulais juste savoir s'il y avait un moyen d'y parvenir ou non :) Mais bien sûr, vous avez un point de vue très valable et je vous remercie de votre perspicacité.

86voto

Vish Desai Points 113

Avec un compilateur compatible avec C++11, vous pouvez faire quelque chose comme ceci :

template<class Derived> class MyClass {

    MyClass() {
        // Compile-time sanity check
        static_assert(std::is_base_of<BaseClass, Derived>::value, "Derived not derived from BaseClass");

        // Do other construction related stuff...
        ...
   }
}

J'ai testé ceci en utilisant le compilateur gcc 4.8.1 dans un environnement CYGWIN - cela devrait donc fonctionner dans les environnements *nix également.

0 votes

Pour moi, ça marche aussi comme ça : template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity"); ...

1 votes

Je pense que c'est la réponse la plus lisible qui évite le code supplémentaire au moment de l'exécution.

57voto

sepp2k Points 157757

Dans ce cas, vous pouvez le faire :

template <class T> void function(){
    Baseclass *object = new T();

}

Cela ne compilera pas si T n'est pas une sous-classe de Baseclass (ou T est Baseclass).

0 votes

Ah oui, c'est une bonne idée. Merci ! Je suppose qu'il n'y a donc aucun moyen de le définir dans la définition du modèle ?

3 votes

@phant0m : Correct. Vous ne pouvez pas contraindre explicitement les paramètres des modèles (sauf en utilisant des concepts, ce qui a été envisagé pour c++0x mais ensuite abandonné). Toutes les contraintes se produisent implicitement par les opérations que vous effectuez sur lui (ou en d'autres termes, la seule contrainte est "Le type doit supporter toutes les opérations qui sont effectuées sur lui").

8 votes

Cela exécute le constructeur T(), et nécessite l'existence du constructeur T(). Voir ma réponse pour une façon d'éviter ces exigences.

53voto

Douglas Leeder Points 29986

Pour exécuter moins de code inutile au moment de l'exécution, vous pouvez consulter : http://www.stroustrup.com/bs_faq2.html#constraints qui fournit des classes permettant d'effectuer le test de compilation de manière efficace et de produire des messages d'erreur plus agréables.

En particulier :

template<class T, class B> struct Derived_from {
        static void constraints(T* p) { B* pb = p; }
        Derived_from() { void(*p)(T*) = constraints; }
};

template<class T> void function() {
    Derived_from<T,Baseclass>();
}

2 votes

Pour moi, c'est la meilleure et la plus intéressante des réponses. N'oubliez pas de consulter la FAQ de Stroustrup pour en savoir plus sur toutes sortes de contraintes que vous pourriez appliquer de la même manière.

1 votes

En effet, c'est une sacrée réponse ! Merci. Le site mentionné est déplacé ici : stroustrup.com/bs_faq2.html#contraintes

0 votes

C'est une excellente réponse. Existe-t-il de bons moyens d'éviter les avertissements unused variable 'p' y unused variable 'pb' ?

11voto

Vous n'avez pas besoin de concepts, mais vous pouvez utiliser SFINAE :

template <typename T>
boost::enable_if< boost::is_base_of<Base,T>::value >::type function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

Notez que cette méthode n'instancie la fonction que lorsque la condition est remplie, mais qu'elle ne fournit pas d'erreur sensée si la condition n'est pas remplie.

0 votes

Et si vous enveloppiez toutes les fonctions de cette façon ? Que retourne-t-il ?

0 votes

El enable_if prend un second paramètre de type qui prend la valeur par défaut de void . T enable_if< true, int >::type représente le type int . Je ne comprends pas vraiment votre première question. Vous pouvez utiliser SFINAE pour ce que vous voulez, mais je ne comprends pas bien ce que vous avez l'intention de faire avec cette fonction globale.

4voto

Daniel Trebbien Points 18089

Vous pourriez utiliser Contrôle du concept Boost 's BOOST_CONCEPT_REQUIRES :

#include <boost/concept_check.hpp>
#include <boost/concept/requires.hpp>

template <class T>
BOOST_CONCEPT_REQUIRES(
    ((boost::Convertible<T, BaseClass>)),
(void)) function()
{
    //...
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X