Let's assume this class hierarchy below.
class BaseClass {
public:
int x;
}
class SubClass1 : public BaseClass {
public:
double y;
}
class SubClass2 : public BaseClass {
public:
float z;
}
...
I want to make a heterogeneous container of these classes. Since the subclasses are derived from the base class I can make something like this:
std::vector<BaseClass*> container1;
But since C++17 I can also use std::variant
like this:
std::vector<std::variant<SubClass1, SubClass2, ...>> container2;
What are the advantages/disadvantages of using one or the other? I am interested in the performance too.
Take into consideration that I am going to sort the container by x
, and I also need to be able to find out the exact type of the elements. I am going to
x
,std::variant<A,B,C>
holds one of a closed set of types. You can check whether it holds a given type with std::holds_alternative
, or use std::visit
to pass a visitor object with an overloaded operator()
. There is likely no dynamic memory allocation, however, it is hard to extend: the class with the std::variant
and any visitor classes will need to know the list of possible types.
On the other hand, BaseClass*
holds an unbounded set of derived class types. You ought to be holding std::unique_ptr<BaseClass>
or std::shared_ptr<BaseClass>
to avoid the potential for memory leaks. To determine whether an instance of a specific type is stored, you must use dynamic_cast
or a virtual
function. This option requires dynamic memory allocation, but if all processing is via virtual
functions, then the code that holds the container does not need to know the full list of types that could be stored.