Reputation: 53
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.
std::unique_pointer<...>::element_type
and std::pointer_traits
but could not get it running.Any ideas?
Upvotes: 1
Views: 1062
Reputation: 73446
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 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
}
Upvotes: 1
Reputation: 2038
Using the visitor pattern
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;
}
Output 1 1
Upvotes: 1