Use case of uvm_void

I was reading about the use of uvm_void from the Accellera forum,LINK where it is mentioned that it “destroys type safety” by acting like a universal base class, similar to void* in C++. However, I am not able to fully understand what this means.

What exactly is “type safety” in this context, and how does uvm_void affect it? Why is having a universal base class considered unsafe?

Also, in UVM, components exist for the entire simulation, while transactions (objects) exist only temporarily. So what is the purpose of using something like uvm_void? Is it related to destroying objects, or does it serve some other purpose entirely?

Could someone explain the practical use case of uvm_void and why it is needed?

In C++, the void* pointer is highly unsafe because it enables access to memory that may not be permitted. uvm_void behaves similarly to the *root object class in Java or Python. This weakens type safety by shifting checks from the compiler to the runtime, which can result in crashes.

The primary purpose of a root class, and likely the reason behind its inclusion in the UVM, was to facilitate the merging of various frameworks. However, this functionality would have been more intuitive if it had been a language feature. There are alternative approaches to achieving this, such as using constructs like an interface class to indicate that a class is required to implement a copy() method.