Check if the class is polymorphic

We have a subproject 'commonUtils' in which there are many common code snippets used in the parent project. One such interesting thing that I saw was: -

/********************************************************************* 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; } 

I believed the comment and thought it was a pretty interesting template, although this is not used throughout the project. I tried to use this just for curiosity ...

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

But not one of them ever returns the truth. MSVC 2005 does not give any warnings, but Comeau warns that the typeid expression has no effect. Section 5.2.8 in the C ++ standard does not say anything similar to what the comment says, i.e. typeid is evaluated at compile time for non-polymorphic types and at runtime for polymorphic types.

1) So I think the comment is misleading / just wrong, or since the author of this code is a pretty senior C ++ programmer, did I miss something?

2) GTR, I wonder if we can check if the class is polymorphic (has at least one virtual function) using some kind of technique?

3) When would one know if a class is polymorphic? Rough assumption; to get the starting address of the class using dynamic_cast<void*>(T) (since dynamic_cast only works with polymorphic classes).

Waiting for your opinion.

Thanks in advance,

+8
c ++ polymorphism templates
source share
5 answers

I cannot imagine how this type can be used to verify that the type is polymorphic. It cannot even be used to claim that this is so, since typeid will work on any type. Boost has an implementation here . As for why this might be needed - in one case I know the Boost.Serialization library. If you save a non-polymorphic type, you can just save it. If you keep the polymorphic, you need to get its dynamic type using typeid, and then call the serialization method for that type (search it in some table).

Update : Looks like I'm wrong. Consider this option:

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

This really works, as the name suggests, exactly for the comment in the source code snippet. An expression inside a typeid is not evaluated if it "does not denote the lvalue of the type of the polymorphic class" (std 3.2 / 2). So, in the case above, if T is not polymorphic, the typeid expression is not evaluated. If T is polymorphic, then * t is indeed a value of the polymorphic type, so the whole expression must be evaluated.

Now your original example is still wrong :-). He used T() , not *t . And T() create an rvalue (std 3.10 / 6). Thus, it still gives an expression that is not a “polymorphic class value”.

This is a pretty interesting trick. On the other hand, its practical value is somewhat limited - since while boost :: is_polymorphic gives you a compile-time constant, it gives you a run-time value, so you cannot instantiate other code for polymorphic and non-polymorphic types.

+8
source share
 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; } 
+3
source share

Starting with C ++ 11, it is now available in the <type_traits> header as std::is_polymorphic . This can be used like this:

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

It prints simply "PolyBase = Polymorphic".

+1
source share

You can use facts that:

  1. dynamic_cast fails at compile time if the argument is not a polymorphic class. So it can be used with SFINAE.
  2. dynamic_cast<void*> is a valid cast that returns the address of a complete polymorphic object.

Therefore, in 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. } 
0
source share

I'm a little confused here, and I hope to get some comments on this answer, explaining what I am missing.

Of course, if you want to find out if a class is polymorphic, all you have to do is ask if it supports dynamic_cast , right?

 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*> {}; 

Can anyone point out the drawback of this implementation? I assume that in the past it should have been one, or must have been one, because the Boost documentation continues to claim that is_polymorphic msgstr "C ++ portability cannot be performed."

But "portable" is some kind of affectionate word, right? Perhaps they simply refer to how MSVC does not support the-SFINAE expression, or some dialects, such as Embedded C ++, do not support dynamic_cast . Perhaps when they say "C ++ language", they mean "a subset of the lowest common denominator of the C ++ language." But I have a suspicious suspicion that maybe they mean what they say, and I'm the one who missed something.

The typeid approach in OP (adjusted for the later response to using lvalue rather than rvalue) also seems fine, but of course it is not constexpr and requires the actual construction of T , which can be super expensive. So this dynamic_cast approach seems better ... if it doesn't work for some reason. Thoughts?

-one
source share

All Articles