I have a number of unrelated types that all support the same operations through overloaded free functions (ad hoc polymorphism):
struct A {};
void use(int x) { std::cout << "int = " << x << std::endl; }
void use(const std::string& x) { std::cout << "string = " << x << std::endl; }
void use(const A&) { std::cout << "class A" << std::endl; }
As the title of the question implies, I want to store instances of those types in an heterogeneous container so that I can use()
them no matter what concrete type they are. The container must have value semantics (ie. an assignment between two containers copies the data, it doesn't share it).
std::vector<???> items;
items.emplace_back(3);
items.emplace_back(std::string{ "hello" });
items.emplace_back(A{});
for (const auto& item: items)
use(item);
// or better yet
use(items);
And of course this must be fully extensible. Think of a library API that takes a vector<???>
, and client code that adds its own types to the already known ones.
The usual solution is to store (smart) pointers to an (abstract) interface (eg. vector<unique_ptr<IUsable>>
) but this has a number of drawbacks -- from the top of my head:
- I have to migrate my current ad hoc polymorphic model to a class hierarchy where every single class inherits from the common interface. Oh snap! Now I have to write wrappers for
int
andstring
and what not... Not to mention the decreased reusability/composability due to the free member functions becoming intimately tied to the interface (virtual member functions). - The container loses its value semantics: a simple assignment
vec1 = vec2
is impossible if we useunique_ptr
(forcing me to manually perform deep copies), or both containers end up with shared state if we useshared_ptr
(which has its advantages and disadvantages -- but since I want value semantics on the container, again I am forced to manually perform deep copies). - To be able to perform deep copies, the interface must support a virtual
clone()
function which has to be implemented in every single derived class. Can you seriously think of something more boring than that?
To sum it up: this adds a lot of unnecessary coupling and requires tons of (arguably useless) boilerplate code. This is definitely not satisfactory but so far this is the only practical solution I know of.
I have been searching for a viable alternative to subtype polymorphism (aka. interface inheritance) for ages. I play a lot with ad hoc polymorphism (aka. overloaded free functions) but I always hit the same hard wall: containers have to be homogeneous, so I always grudgingly go back to inheritance and smart pointers, with all the drawbacks already listed above (and probably more).
Ideally, I'd like to have a mere vector<IUsable>
with proper value semantics, without changing anything to my current (absence of) type hierarchy, and keep ad hoc polymorphism instead of requiring subtype polymorphism.
Is this possible? If so, how?