Question

Nous avons un sous-projet « commonUtils » qui a de nombreux génériques de code-extraits utilisés dans le projet parent. L'une de ces choses intéressantes, j'ai vu était: -

/*********************************************************************
If T is polymorphic, the compiler is required to evaluate the typeid 
stuff at runtime, and answer will be true.  If T is non-polymorphic, 
the compiler is required to evaluate the typeid stuff at compile time, 
whence answer will remain false
*********************************************************************/
template <class T> 
bool isPolymorphic() { 
   bool answer=false; 
   typeid(answer=true,T()); 
   return answer; 
}

Je croyais le commentaire et je pensais qu'il est tout à fait un modèle intéressant qu'il ne soit pas utilisé dans le projet. J'ai essayé de l'utiliser comme ça juste pour la curiosité ...

class PolyBase {
public:
   virtual ~PolyBase(){}
};

class NPolyBase {
public:
   ~NPolyBase(){}
};


if (isPolymorphic<PolyBase>())
  std::cout<<"PolyBase = Polymorphic\n";
if (isPolymorphic<NPolyBase>())
  std::cout<<"NPolyBase = Also Polymorphic\n";

Mais aucun de ceux qui ne retourne jamais vrai. MSVC 2005 donne aucun avertissement, mais met en garde contre l'expression Comeau typeid n'a pas d'effet. Section 5.2.8 dans la norme C de ne dit rien comme ce que le commentaire dit savoir typeid est est évaluée au moment de la compilation pour les types non polymorphes et lors de l'exécution pour les types polymorphes.

1) donc je suppose que le commentaire est trompeur / plain-mal ou que l'auteur de ce code est un très haut programmeur C ++, suis-je manque quelque chose?

2) OTOH, je me demande si nous pouvons vérifier si une classe est polymorphes (a au moins une fonction virtuelle) en utilisant une technique?

3) Quand voudrait-on savoir si une classe est polymorphique? conjecture sauvage; pour obtenir le démarrage adresse d'une classe en utilisant dynamic_cast<void*>(T) (comme dynamic_cast ne fonctionne que sur les classes polymorphes).

En attente de vos opinions.

Merci à l'avance,

Était-ce utile?

La solution

Je ne peux imaginer aucune façon possible comment cela typeid pourrait être utilisé pour vérifier ce type est polymorphes. Il ne peut même pas être utilisé pour affirmer qu'il est, puisque typeid fonctionnera sur tout type. Boost a une mise en œuvre . Quant à savoir pourquoi il peut être nécessaire - un cas que je connais est la bibliothèque Boost.Serialization. Si vous enregistrez type non polymorphes, alors vous pouvez simplement l'enregistrer. Si vous enregistrez un polymorphes, vous devez obtient son type dynamique à l'aide typeid, puis invoquez méthode de sérialisation pour ce type (le regardant dans une tableau).

Mise à jour : il semble que je suis réellement mal. Considérez cette variante:

template <class T> 
bool isPolymorphic() { 
    bool answer=false;
    T *t = new T();
    typeid(answer=true,*t); 
    delete t;
    return answer; 
}

Cela ne fait travailler comme nom l'indique, exactement par commentaire dans l'extrait de code d'origine. L'expression à l'intérieur typeid n'a pas évalué si elle « ne désigne pas une lvalue de type de classe polymorphe » (std 3,2 / 2). Ainsi, dans le cas ci-dessus, si T est pas polymorphes, l'expression typeid n'est pas évaluée. Si T est polymorphique, alors * t est en effet lvalue de type polymorphes, donc l'expression entière doit être évaluée.

Maintenant, votre exemple original est encore mal :-). Il fut T(), pas *t. Et T() créer rvalue (std 3.10 / 6). Ainsi, il donne encore une expression qui n'est pas « lvalue de classe polymorphes ».

C'est assez truc intéressant. D'autre part, sa valeur pratique est quelque peu limitée - car tout boost :: is_polymorphic vous donne une constante de compilation, celui-ci vous donne une valeur d'exécution, de sorte que vous ne pouvez pas instancier code différent pour les types polymorphes et non polymorphes .

Autres conseils



class PolyBase {
public:   
    virtual ~PolyBase(){}
};

class NPolyBase {
public:
    ~NPolyBase(){}
};

template<class T>
struct IsPolymorphic
{
    struct Derived : T {
        virtual ~Derived();
    };
    enum  { value = sizeof(Derived)==sizeof(T) };
};


void ff()
{
    std::cout << IsPolymorphic<PolyBase >::value << std::endl;
    std::cout << IsPolymorphic<NPolyBase>::value << std::endl;
}

Depuis le 11 C de c'est maintenant disponible dans l'en-tête de <type_traits> comme std::is_polymorphic. Il peut être utilisé comme ceci:

struct PolyBase {
  virtual ~PolyBase() {}
};

struct NPolyBase { 
  ~NPolyBase() {}
};

if (std::is_polymorphic<PolyBase>::value)
  std::cout << "PolyBase = Polymorphic\n";
if (std::is_polymorphic<NPolyBase>::value)
  std::cout << "NPolyBase = Also Polymorphic\n";

Cette impression juste "POLYBASE = polymorphes".

On peut utiliser les faits que:

  1. dynamic_cast échoue au moment de la compilation si l'argument n'est pas une classe polymorphes. Alors qu'il peut être utilisé avec SFINAE.
  2. dynamic_cast<void*> est un lancer valide qui retourne l'adresse du complet objet polymorpic.

Par conséquent, en C ++ 11:

#include <iostream>
#include <type_traits>

template<class T>
auto is_polymorphic2_test(T* p) -> decltype(dynamic_cast<void*>(p), std::true_type{});

template<class T>
auto is_polymorphic2_test(...) -> std::false_type;

template<class T>
using is_polymorphic2 = decltype(is_polymorphic2_test<T>(static_cast<T*>(0)));

struct A {};
struct B { virtual ~B(); };

int main() {
    std::cout << is_polymorphic2<A>::value << '\n'; // Outputs 0.
    std::cout << is_polymorphic2<B>::value << '\n'; // Outputs 1.
}

Je suis un peu confus ici, et je espère obtenir des commentaires sur cette réponse en expliquant ce que je suis absent.

Certes, si vous voulez savoir si une classe est polymorphique, tout ce que vous avez à faire est de demander si elle prend en charge dynamic_cast, est-ce pas?

template<class T, class> struct is_polymorphic_impl   : false_type {};
template<class T> struct is_polymorphic_impl
    <T, decltype(dynamic_cast<void*>(declval<T*>()))> : true_type {};

template<class T> struct is_polymorphic :
    is_polymorphic_impl<remove_cv_t<T>, void*> {};

Quelqu'un peut-il signaler une faille dans cette mise en œuvre? Je suppose qu'il doit y avoir un, ou doit avoir été un à un moment donné dans le passé, parce que la documentation Boost continue de prétendre que is_polymorphic "ne peut pas être mis en œuvre dans le portably langage C ++".

Mais « portably » est une sorte de mot de belette, non? Peut-être qu'ils sont juste en faisant allusion à la façon dont MSVC ne supporte pas l'expression-SFINAE, ou certains dialectes tels que C ++ embarqué ne prennent pas en charge dynamic_cast. Peut-être que quand ils disent « le langage C ++ » ils signifient « un sous-ensemble plus petit dénominateur commun du langage C ++. » Mais je soupçonne que lancinante peut-être qu'ils veulent dire ce qu'ils disent, et Je suis celui qui manque quelque chose.

L'approche typeid dans l'OP (tel que modifié par une réponse plus tard d'utiliser un lvalue pas rvalue) semble aussi très bien, mais bien sûr, ce n'est pas constexpr et il faut construire en fait un T, ce qui pourrait être super cher. Donc, cette approche semble dynamic_cast mieux ... à moins que cela ne fonctionne pas pour une raison quelconque. Pensées?

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