Say I have a templated class that wraps its template argument to provide some extra functionality, like the ability to persist the object's state to disk:
template<typename T>
class Persistent {
std::unique_ptr<T> wrapped_obj;
public:
Persistent(std::unique_ptr<T> obj_to_wrap);
void take_snapshot(int version);
void save(int to_version);
void load(int to_version);
}
I want to have another class, let's call it PersistentManager, store a list of these templated Persistent objects and call their member methods without knowing their template parameters. There are two ways I can see to do that: use std::function to erase the template type from each method, or use an abstract base class and virtual function calls.
Using std::function, each Persistent object would be capable of returning a bundle of std::functions bound to its members:
struct PersistentAPI {
std::function<void(int)> take_snapshot;
std::function<void(int)> save;
std::function<void(int)> load;
}
template<typename T>
PersistentAPI Persistent<T>::make_api() {
using namespace std::placeholders;
return {std::bind(&Persistent<T>::take_snapshot, this, _1),
std::bind(&Persistent<T>::save, this, _1),
std::bind(&Persistent<T>::load, this, _1)}
}
Then the PersistentManager can store a list of PersistentAPIs, and have a method like this:
void PersistentManager::save_all(int version) {
for(PersistentAPI& bundle : persistents) {
bundle.save(version);
}
}
Using inheritance, I would create an abstract class with no template parameters that defines each of Persistent's methods as virtual, and make Persistent inherit from it. Then the PersistentManager can store pointers to this base class, and call the Persistent methods through virtual function calls:
class AbstractPersistent {
public:
virtual void take_snapshot(int version) = 0;
virtual void save(int to_version) = 0;
virtual void load(int to_version) = 0;
}
template<typename T>
class Persistent : public AbstractPersistent {
...
}
void PersistentManager::save_all(int version) {
for(AbstractPersistent* obj : persistents) {
obj->save(version);
}
}
Both of these approaches add some overhead to the function call from PersistentManager: rather than dispatching the function call directly to a Persistent instance, they require going through an intermediary layer, either the std::function object or the virtual function table in AbstractPersistent.
My question is, which approach adds less overhead? Since these are both fairly opaque parts of the standard library, I don't have a good sense of how "expensive" a std::function call is compared to a virtual function call through a base class pointer.
(I've found a few other questions on this site asking about the overhead of std::function, but they all lack a specific alternative to compare against.)