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?
Boost.Any
help you? – Adverbboost::any
. I can see how it stores unrelated types, but to actuallyuse()
the underlying object one has to know its type first, right? If so, that kinda defeats the point (unless, of course, I missed something important along the way). – Ranjivuse
on them, you can usestd::vector<std::function<void()>>
and havetemplate<typename T> std::function<void()> make_use_fn(T&& v) { return [v=std::forward<T>(v)]{ return use(v); }; }
to doitems.push_back(make_use_fn(3));
items.push_back(make_use_fn(std::string{ "hello" }));
– Disjointed