假设我有一个模板类,它包装了它的模板参数以提供一些额外的功能,比如将对象的状态保存到磁盘的能力:
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);
}
我想要另一个类,我们称之为 PersistentManager,存储这些模板化的 Persistent 对象的列表,并在不知道它们的模板参数的情况下调用它们的成员方法。我可以看到有两种方法可以做到这一点:使用 std::function 从每个方法中删除模板类型,或者使用抽象基类和虚函数调用。
使用 std::function,每个 Persistent 对象将能够返回绑定到其成员的 std::functions 包:
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)}
}
然后 PersistentManager 可以存储PersistentAPI
s 的列表,并且有这样的方法:
void PersistentManager::save_all(int version) {
for(PersistentAPI& bundle : persistents) {
bundle.save(version);
}
}
使用继承,我将创建一个没有模板参数的抽象类,它将 Persistent 的每个方法都定义为虚拟方法,并使 Persistent 继承自它。然后 PersistentManager 可以存储指向这个基类的指针,并通过虚函数调用来调用 Persistent 方法:
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);
}
}
这两种方法都为 PersistentManager 的函数调用增加了一些开销:它们不需要将函数调用直接分派到 Persistent 实例,而是需要通过中间层,要么是 std::function 对象,要么是 AbstractPersistent 中的虚函数表。
我的问题是,哪种方法增加的开销更少?由于这些都是标准库中相当不透明的部分,因此我不太了解 std::function 调用与通过基类指针进行的虚函数调用相比有多“昂贵”。
(我在这个网站上发现了一些其他关于 std::function 开销的问题,但它们都缺乏一个具体的替代方案来比较。)