1

在我的几个项目中,我越来越需要处理内存中的连续位序列 - 高效(*)。到目前为止,我已经编写了一堆可内联的独立函数,以选择“位容器”类型(例如uint32_t)为模板,用于获取和设置位,将“或”和“与”应用于它们的值,定位容器,以位为单位的长度转换为以字节为单位的大小或容器中的长度等......看起来这是编写类的时间。

我知道 C++ 标准库有一个专门化的std::vector<bool>,这被许多人认为是一个设计缺陷——因为它的迭代器不暴露实际bool的 s,而是代理对象。无论这对于专业化来说是一个好主意还是一个坏主意,这绝对是我正在考虑的东西 - 一个显式的位代理类,希望它“总是”被优化掉(用constexpr,noexcept和很好地润滑inline)。因此,我正在考虑可能std::vector从标准库实现之一改编代码。

另一方面,我的预期课程:

  • 永远不会拥有数据/位-它将接收起始位容器地址(假设对齐)和位长度,并且不会分配或释放。
  • 它将无法动态或以其他方式调整数据大小 - 即使在保留与 std::vector::resize() 相同的空间量时也不行;它的长度将在其生命周期/范围内固定。
  • 它不应该对堆一无所知(并且在没有堆时工作)

从这个意义上说,它更像是位的跨度类。那么也许从跨度开始呢?我不知道,跨度仍然不标准;并且跨度中没有代理...

那么什么是我实现的良好基础(编辑:不是基类)?std::vector<bool>? std::span? 两个都?没有任何?或者 - 也许我正在重新发明轮子,这已经是一个已解决的问题?

笔记:

  • 位序列长度在运行时是已知的,而不是编译时;否则,正如@SomeProgrammerDude 建议的那样,我可以使用std::bitset.
  • 我的班级不需要“成为”跨度或“成为”向量,所以我没有考虑专门研究其中任何一个。

(*) - 到目前为止,SIMD 效率不高,但可能会在以后出现。此外,这可以在我们不 SIMDize 而是假装通道是正确线程的 CUDA 代码中使用。

4

1 回答 1

1

而不是std::vector或者std::span我怀疑您的类的实现将与 共享更多共同点std::bitset,因为它几乎是相同的东西,除了(固定)运行时确定的大小。

事实上,您可能会采用典型的std::bitset实现并将<size_t N>模板参数作为size_t size_成员(或您喜欢的任何名称)移动到类中,并且您将拥有几乎没有任何更改的动态 bitset 类。你可能想摆脱任何你认为不重要的东西,比如构造函数std::string和朋友。

最后一步是删除底层数据的所有权:基本上,您将在构造函数中删除底层数组的创建,并使用一些指针维护现有数组的视图。

如果您的客户不同意用于存储的底层无符号整数类型(您称之为“位容器”),那么您可能还需要使您的类成为这种类型的模板,尽管如果每个人都同意会更简单说uint64_t

就目前std::vector<bool>而言,你不需要太多:vector你想做的一切,std::bitset可能也会做:vector增加的主要是动态增长——但你说过你不想要那个。vector<bool>具有代表单个位的代理对象概念,但std::bitset.

std::span您的基础数据的非所有权的想法,但我不认为这实际上代表了很多基础代码。如果这对您有用(主要是如果您有时使用编译时大小并且可以将某些std::span方法专门化为更多在这种情况下有效)。Extent == std::dynamic_extent

于 2018-06-15T03:00:50.860 回答