gpt4 book ai didi

c++ - 如何设计符合标准的 std::any 实现的存储?

转载 作者:可可西里 更新时间:2023-11-01 18:00:02 26 4
gpt4 key购买 nike

标准工作草案(n4582、20.6.3、p.552)对 std::any 的实现提出了以下建议:

Implementations should avoid the use of dynamically allocated memory for a small contained object. [ Example: where the object constructed is holding only an int. —end example ] Such small-object optimization shall only be applied to types T for which is_nothrow_move_constructible_v is true.

据我所知,std::any 可以通过类型删除/虚函数和动态分配内存轻松实现。

如果在销毁时不知道编译时信息,std::any 如何避免动态分配并仍然销毁这些值;如何设计遵循标准建议的解决方案?


如果有人想查看非动态部分的可能实现,我已经在 Code Review 上发布了一个:https://codereview.stackexchange.com/questions/128011/an-implementation-of-a-static-any-type

这里的答案有点太长了。它基于 Kerrek SB 对以下评论的建议。

最佳答案

通常,any接受任何东西并从中动态分配一个新对象:

struct any {
placeholder* place;

template <class T>
any(T const& value) {
place = new holder<T>(value);
}

~any() {
delete place;
}
};

我们使用 placeholder 的事实是多态的,可以处理我们所有的操作——销毁、转换等。但是现在我们想避免分配,这意味着我们避免了多态性给我们带来的所有好处——并且需要重新实现它们。首先,我们将有一些 union :

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 膨胀带有一堆函数指针的大小。这样,我们会损失一点点性能,以换取尺寸上的巨大差异。

在构造时,我们然后填充 storagevtable :

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>>;

然后我们就使用那个虚表来实现我们的各种操作。喜欢:

~any() {
vtable(OP_DESTROY, storage, nullptr);
}

关于c++ - 如何设计符合标准的 std::any 实现的存储?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37094710/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com