通常,any
获取任何东西并从中动态分配一个新对象:
struct any {
placeholder* place;
template <class T>
any(T const& value) {
place = new holder<T>(value);
}
~any() {
delete place;
}
};
我们使用多态这一事实placeholder
来处理我们所有的操作——销毁、强制转换等。但是现在我们想要避免分配,这意味着我们要避免多态给我们带来的所有好处——并且需要重新实现它们。首先,我们将有一些联合:
union Storage {
placeholder* ptr;
std::aligned_storage_t<sizeof(ptr), sizeof(ptr)> buffer;
};
我们有一些template <class T> is_small_object { ... }
来决定我们是否正在做ptr = new holder<T>(value)
或new (&buffer) T(value)
。但是构造并不是我们唯一要做的事情——我们还必须进行破坏和类型信息检索,这取决于我们所处的情况而有所不同。要么我们正在做,要么delete ptr
我们正在做static_cast<T*>(&buffer)->~T();
,后者这取决于跟踪T
!
所以我们引入了我们自己的类似 vtable 的东西。然后我们any
将坚持:
enum Op { OP_DESTROY, OP_TYPE_INFO };
void (*vtable)(Op, Storage&, const std::type_info* );
Storage storage;
相反,您可以为每个操作创建一个新的函数指针,但我可能在这里缺少其他几个操作(例如OP_CLONE
,这可能需要将传入的参数更改为union
...)并且您不需要只想any
用一堆函数指针来膨胀你的大小。通过这种方式,我们损失了一点点性能,以换取尺寸上的巨大差异。
在构建时,我们会同时填充storage
和vtable
:
template <class T,
class dT = std::decay_t<T>,
class V = VTable<dT>,
class = std::enable_if_t<!std::is_same<dT, any>::value>>
any(T&& value)
: vtable(V::vtable)
, storage(V::create(std::forward<T>(value))
{ }
我们的VTable
类型类似于:
template <class T>
struct PolymorphicVTable {
template <class U>
static Storage create(U&& value) {
Storage s;
s.ptr = new holder<T>(std::forward<U>(value));
return s;
}
static void vtable(Op op, Storage& storage, const std::type_info* ti) {
placeholder* p = storage.ptr;
switch (op) {
case OP_TYPE_INFO:
ti = &typeid(T);
break;
case OP_DESTROY:
delete p;
break;
}
}
};
template <class T>
struct InternalVTable {
template <class U>
static Storage create(U&& value) {
Storage s;
new (&s.buffer) T(std::forward<U>(value));
return s;
}
static void vtable(Op op, Storage& storage, const std::type_info* ti) {
auto p = static_cast<T*>(&storage.buffer);
switch (op) {
case OP_TYPE_INFO:
ti = &typeid(T);
break;
case OP_DESTROY:
p->~T();
break;
}
}
};
template <class T>
using VTable = std::conditional_t<sizeof(T) <= 8 && std::is_nothrow_move_constructible<T>::value,
InternalVTable<T>,
PolymorphicVTable<T>>;
然后我们只是使用那个 vtable 来实现我们的各种操作。喜欢:
~any() {
vtable(OP_DESTROY, storage, nullptr);
}