You can impement polymorphic value types if you want.
Unless you need insane extendibility, dynamic cast is going to be a poor performing option; it is a nuclear bomb to kill a termite.
An easy way is to make your node a std::variant of possible node types. You can add a "get base class" helper if you need it (and cache it for speed).
For moderate extendibility, a std any is a value type that lets you cast to exact type.
Or you can implement your own downcast table if you prefer, and keep storing pointers.
Myself, I'd use the variant approach. Nodes containing nodes takea a bit of finess (as variant needs the full class definition, and a node is a variant of each node type), bit there are a lot of solutions (node is actually a thin class that inherits from, or contains a variant, for example).
Using C++ dynamic cast requires a virtual method. But you can make the destructor virtual to satisfy that.
template<class Base, class...Ts> requires (std::is_base_of_v<Base, Ts>&&...)
struct poly_variant:std::variant<Ts...>{
Base& base(){ return std::visit([](auto&&elem)->Base&{return elem;}, *this); }
Base const& base()const{ return std::visit([](auto&&elem)->Base const&{return elem;}, *this); }
using std::variant<Ts...>::variant;
};
struct node;
// define base_avian
// define duck, chicken, flock
struct node : poly_variant<base_avian, chicken, duck, flock> {
using poly_variant<base_avian, chicken, duck, flock>::poly_variant;
};
there. Now you can
std::vector<node> children;
for(auto&child:children){
child.base().SomeAvianMethod();
flock* f=std::fet_if<flock>(&child);
}
no vtable in sight.
Of course if your base_avian type is empty you can ignore most of this.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…