Decltype производного класса от уникального указателя на базовый класс - PullRequest
1 голос
/ 02 ноября 2019

Я хочу выполнить полиморфизм во время выполнения и должен также знать фактический тип, например, для подсчета экземпляров определенного производного класса. Вот что у меня есть:

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

Проблема в том, что я управляю толькочтобы получить тип базового класса. Не производный. Поэтому я никогда не вношу оператор if.

  • Я мог бы добавить виртуальную функцию «GetID ()» или аналогичную. Но я чувствую, что это излишне. Я должен быть в состоянии сделать это с чертами типа?
  • Я изучил std::unique_pointer<...>::element_type и std::pointer_traits, но не смог запустить его.

Есть идеи?

Ответы [ 2 ]

0 голосов
/ 05 ноября 2019

Проблема в вашем коде состоит в том, что вы ожидаете полиморфизм во время выполнения, но используете только типы времени компиляции:

  • decltype(x) дает вам время компиляции тип x
  • , поэтому typeid(decltype(x)) относится к информации о типе среды выполнения типа времени компиляции, поэтому 4Base в вашем примере.

Если вы хотите использоватьтип времени выполнения, используйте typeid непосредственно для объекта следующим образом:

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

Результат будет выглядеть следующим образом:

St10unique_ptrI4BaseSt14default_deleteIS0_EE
10DerivedFoo
10DerivedFoo
St10unique_ptrI4BaseSt14default_deleteIS0_EE
10DerivedBar
10DerivedBar

Таким же образом, std::is_same<DerivedFoo, decltype(elem)>::value основан на выводе типа шаблона, поэтому время компиляции снова. Если вы хотите проверить это на полиморфном типе во время выполнения, вам нужно использовать следующую конструкцию:

if (dynamic_cast<DerivedFoo*>(&*elem)) {
    // do something
}
0 голосов
/ 02 ноября 2019

Использование шаблона посетителя

struct Counter;
struct Foo;
struct Bar;
struct BarBar;

struct Counter {
    int barCount = 0;
    int barBarCount = 0;

    void visit(Bar *bar) {
        barCount += 1;
    }
    void visit(BarBar *bar) {
        barBarCount += 1;
    }
};

struct Foo {
    virtual void accept(Counter* v) = 0;
};

struct Bar : public Foo {
    void accept(Counter* v) override {
        v->visit(this);
    }
};

struct BarBar : public Foo {
    void accept(Counter* v) override {
        v->visit(this);
    }
};


int main()
{
    std::vector<std::unique_ptr<Foo>> foos;
    foos.emplace_back(std::make_unique<Bar>());
    foos.emplace_back(std::make_unique<BarBar>());

    Counter counter;

    for(const auto& foo : foos) {
        foo->accept(&counter);
    }

    std::cout << counter.barCount << " " << counter.barBarCount << std::endl;
}

Вывод 1 1

...