c++stlcontainersc++17heterogeneous

std::variant vs pointer to base class for heterogeneous containers in C++


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

  1. Fill the container,
  2. Sort it by x,
  3. Iterate through all the elements, find out the type, use it accordingly,
  4. Clear the container, then the cycle starts over again.

Solution

  • 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.