Test whether a class is polymorphic



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


One can use the facts that:

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

Hence, 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.
}

I cannot imagine any possible way how that typeid could be used to check that type is polymorphic. It cannot even be used to assert that it is, since typeid will work on any type. Boost has an implementation here. As for why it might be necessary -- one case I know is the Boost.Serialization library. If you are saving non-polymorphic type, then you can just save it. If saving polymorphic one, you have to gets its dynamic type using typeid, and then invoke serialization method for that type (looking it up in some table).

Update: it appears I am actually wrong. Consider this variant:

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

This actually does work as name suggests, exactly per comment in your original code snippet. The expression inside typeid is not evaluated if it "does not designate an lvalue of polymorphic class type" (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 lvalue of polymorphic type, so entire expression has to be evaluated.

Now, your original example is still wrong :-). It used T(), not *t. And T() create rvalue (std 3.10/6). So, it still yields an expression that is not "lvalue of polymorphic class".

That's fairly interesting trick. On the other hand, its practical value is somewhat limited -- because while boost::is_polymorphic gives you a compile-time constant, this one gives you a run-time value, so you cannot instantiate different code for polymorphic and non-polymorphic types.


Since C++11, this is now available in the <type_traits> header as std::is_polymorphic. It 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";

This prints just "PolyBase = Polymorphic".