构建矢量以允许非初始化的存储
building a vector to allow uninitialized storage
假设我想构建一个矢量容器,与std :: vector不同,该容器允许非初始化的存储。容器的使用情况,例如vec <T>
,大致是这样的:
-
用户明确指出,向量应分配n个非初始化元素:
vec <T> a(N, no_init);
-
在某个时候知道数据时,用户使用参数
args...
在n
处明确初始化元素:a.init(n, args...);
-
或等效地,手动构造元素:
new (&a[n]) T(args...);
-
其他操作可以更大量初始化或复制(例如
std::uninitialized_copy
),但这仅是为了方便起见;基本的基础操作是相同的。 -
完成一些任务后,向量可以将一些元素初始化,而另一些元素则不要将。向量没有任何额外的信息,因此最终,在释放内存之前,它无论如何都会破坏所有元素,或者仅根据
T
的不同。
我很确定这可以做到,只有我不确定后果。自然,我们希望这种结构对所有类型的T
都安全,假设用户在构造它之前没有尝试使用非初始化的元素。这听起来像是一个强烈的假设,但是仅在向量范围内访问元素并没有太大的假设,也是如此普遍。
所以我的问题是:
-
像
vec <T> a(no_init)
中一样,允许这种非专业化的操作可以安全?我猜is_pod
还可以,而且很可能is_trivial
也可以。我不想提出比必要的更多约束。 -
是否应该为某些类型持续或仅执行破坏?与上述相同的约束会很好吗?
is_trivially_destructible
怎么样?这个想法是,破坏的元素,反之亦然(不是破坏构造的元素)不应受到伤害。 -
这次尝试中是否存在重大缺陷,除了对用户承担更多责任的风险?
全部要点是,当用户确实需要这样的功能来进行性能时,在泄漏方面,较低级别的解决方案(例如std::get_temporary_buffer
或手动分配)(例如使用operator new()
)可能会更风险。我知道std::vector::emplace_back()
,但这确实不是同一件事。
回答问题:
- 对
T
没有限制:如果它适用于标准容器,则适用于您的。 - 破坏是有条件的,如果
std::is_trivially_destructible<T>
,您可以在静态上禁用它,否则您必须跟踪构造的元素并仅删除实际构造的元素。 - 我没有看到您的想法中的主要缺陷,但是请确保值得:个人介绍您的用例并检查您确实花了很多时间初始化元素。
我假设您将容器作为大小size() * sizeof(T)
的连续内存的块实现。另外,如果必须调用该元素的驱动器,即!std::is_trivially_destructible<T>
,则必须启用额外的存储,例如size()
的std::vector<bool>
元素用于标记破坏的元素。
基本上,如果T
在易于破坏的情况下,您只需在用户询问并且不要打扰销毁任何东西时发起。否则,事情有些棘手,您需要跟踪构建哪个元素,哪些元素是不可分化的,因此您只会破坏所需的内容。
-
上的大小或容器创建:
- 如果
!std::is_trivially_destructible<T>
相应地调整标志大小 - 内存分配
- 可选的初始化,具体取决于用户的要求:
-
no_init
=>如果!std::is_trivially_destructible<T>
,将标志元素称为非判决。否则什么也不做。 -
(Args...)
=>如果std::is_constructible<T, class... Args>
为每个元素调用该构造函数。如果!std::is_trivially_destructible<T>
,则构造的标志元素。
-
- 如果
-
缩减大小或容器破坏:
- 可选破坏:
- 如果
std::is_trivially_destructible<T>
无所事事 - 对于每个元素,如果将其标记为构造,则称其为破坏者
- 如果
- 内存DealLocation
- 如果
!std::is_trivially_destructible<T>
相应地调整标志大小
- 可选破坏:
从性能的角度来看,如果T
在巨大的破坏性上,情况就很棒。如果它具有破坏者,则事情会更加约束:您会获得一些构造函数/击路仪的调用,但是您需要维护其他标志存储 - 最后,是否取决于您的构造函数/驱动器是否足够复杂。
也像注释中建议的一样,您可以只使用基于std::unordered_map
的关联数组,添加size_t vector_size
字段,实现resize
和Override size
。这样,甚至不会存储非直接的元素。另一方面,索引较慢。
- 初始化一个由 p 指向的新 INTSTK,它最多可以存储 m 个
- 在C++中,如果成员引用在其声明中初始化,为什么需要存储空间?
- 为什么std::atomic的默认构造函数不默认初始化底层存储值
- 具有静态存储持续时间的常量初始化变量的初始化顺序
- 静态存储中的内联变量何时初始化?
- 静态存储持续时间初始化
- 如何最好地初始化和存储常量对象
- 使用 k 个键值对为零的存储桶初始化 c++14 unordered_map
- 在不同翻译单元中具有静态存储持续时间的依赖非局部常量浮点变量的常量初始化
- 了解字符数组初始化和数据存储
- Qt:初始化存储库中止与"fatal: Needed a single revision"
- C++中结构的存储持续时间和成员初始化
- 如何在派生类中存储基类初始化期间获得的信息
- C++为对象分配存储而不对其进行初始化
- 构建矢量以允许非初始化的存储
- 如何将用户输入存储在变量中,该变量与默认构造函数中的变量初始化中的值不同
- 存储用于初始化类的模板
- 存储可以使用不同派生类型初始化的基类型成员变量时,请避免使用new
- 如何在不分配更多存储空间的情况下从数组初始化向量
- 将字符存储在用字符串初始化的字符指针中