Search code examples
c++inheritancepolymorphismunique-ptrdecltype

Decltype of derived class from unique pointer to base class


I want to do runtime polymorphism and must know the actual type as well e.g. to count the instances of a certain derived class. Here is what I have so far:

header

struct Base {
  virtual ~Base();
};

struct DerivedFoo : public Base {};
struct DerivedBar : public Base {};

source

Base::~Base() {}

int main() {
  std::vector<std::unique_ptr<Base>> my_array{};
  my_array.emplace_back(std::make_unique<DerivedFoo>());
  my_array.emplace_back(std::make_unique<DerivedBar>());

  for (const auto& elem : my_array) {
    if (std::is_same<DerivedFoo, decltype(elem)>::value) {
      // do something
    }

    // some testing
    std::cout << typeid(decltype(elem)).name() << '\n';
    std::cout << typeid(decltype(*elem)).name() << '\n';
    std::cout << typeid(decltype(*elem.get())).name() << '\n';
  }

  return 0;
}

output

St10unique_ptrI4BaseSt14default_deleteIS0_EE
4Base
4Base
St10unique_ptrI4BaseSt14default_deleteIS0_EE
4Base
4Base

Problem is: I only manage to get the type of the base class. Not the derived one. So I never enter the if statement.

  • I could add a virtual function "GetID()" or similar. But my feeling is that this is redundant. I should be able to do this with type traits?
  • I looked into std::unique_pointer<...>::element_type and std::pointer_traits but could not get it running.

Any ideas?


Solution

  • The problem in you code is that you expect runtime polymorphism but only use compile-time types:

    • decltype(x) gives you the compile-time type of x
    • so typeid(decltype(x)) refers to the runtime type information of the compile-time type, so 4Base in your example.

    If you want to use the run-time type, use typeid directly on the object, as follows:

    std::cout <<< typeid(elem).name() << '\n';
    std::cout << typeid(*elem).name() << '\n';
    std::cout << typeid(*elem.get()).name() << '\n';
    

    The result would then look like:

    St10unique_ptrI4BaseSt14default_deleteIS0_EE
    10DerivedFoo
    10DerivedFoo
    St10unique_ptrI4BaseSt14default_deleteIS0_EE
    10DerivedBar
    10DerivedBar
    

    In the same way, std::is_same<DerivedFoo, decltype(elem)>::value is based on template type deduction, so compile-time again. If you want to check this on a polymorphic type at run time, you need to use the following construct:

    if (dynamic_cast<DerivedFoo*>(&*elem)) {
        // do something
    }