【问题标题】:Max-sized array with normal STL interface, similar to boost::array具有普通 STL 接口的最大数组,类似于 boost::array
【发布时间】:2011-07-22 17:58:40
【问题描述】:

boost 中是否有一个类似于 array 的类,它是一种类似 POD 的类型,带有一个数组,但在容器中提供了可变数量的项目。也就是说,我想说数组最多有 10 个项目,但在任何给定时刻可能包含 0 到 10 个项目。

boost::array 不幸地将size() 修复为常量N,因此它不能作为向量的替代品。我特别希望结构的读者不要知道它是一个静态数组,他们可以像任何其他容器一样使用begin()end()

显然,push_back() 如果超出容量,则必须通过异常处理。

boost 中已有的内容将是首选。

注意:它必须类似POD的数据类型。为清楚起见,整个类数组对象,包括内容(本身将由类 POD 类型)必须是类 POD。这是出于序列化/复制的原因(以及与该序列化/复制相关的性能)。

我的意思是类似于 POD:

  • 具有编译时常量大小
  • 可以安全地 memcpy'd

对于那些说它行不通不可能的人。这没有什么巧妙之处。 boost::array 是一种类似 POD 的类型。只需为当前大小添加一个额外的字段,以使其成为我想要的。我正在寻找一个已经存在的实现(因此经过适当的测试/维护)。

【问题讨论】:

  • 为什么一定要POD类型?这个要求对我来说似乎相当武断。你需要序列化数组还是什么?我不认为你可以拥有一个动态增长的数组并同时让它成为 POD。有很多簿记代码可以有效地处理可以增长的数组。只需查看一个像样的std::vector 实现的源代码。
  • 对此的简单回答是我没有遇到过这样的事情。 AFAIK 的最佳解决方案是实现类似容器的 boost 数组(接口非常简单)并根据需要添加大小信息。
  • 对于每个对动态大小感兴趣的人,我认为 OP 想要的是一个固定大小的堆栈分配数组,它跟踪插入了多少项目(直到它的最大大小) .这是一个相当直接的要求,不幸的是 boost 数组没有满足(即无法测试数组中实际触摸了多少项目)
  • (除了POD部分)不是重复的,而是相关的:stackoverflow.com/questions/3563591/…,在sbi的回答中有一个实现
  • @edA-qa mort-ora-y:我不确定您是否理解 POD 的实际含义,这意味着您的要求使您无​​法回答,因为您要求的是您不知道的东西不需要。基本上对于要成为 POD 的类型,它不能有任何构造函数,但是对于新成员属性大小以保持跟踪实际元素数量的不变性,它必须被初始化,这需要构造函数。为了安全起见,成员也应该是私有的,这是破坏 POD 的另一个原因。如果您对答案投了反对票,请重新考虑

标签: c++ boost


【解决方案1】:

我需要类似的东西,动机是性能,因为缓存位置明显优于动态分配的向量。

我创建了非常简单的自定义类来为我做这件事。

#pragma once
#include <boost/array.hpp>

template<class T, class IndexType, IndexType MaximumSize>
class StaticVector : public boost::array<T, MaximumSize>
{
  IndexType count;
public:
  StaticVector() : count(0) {}
  inline void add(const T& value)
  {
    if (this->full())
      throw std::runtime_error("Can't add (size = %d)\n", MaximumSize);
    (*this)[this->count] = value;
    this->count++;
  }

  inline void addDirty()
  {
    if (this->full())
      throw std::runtime_error("Can't add, container full.");
    this->count++;
  }

  inline void remove(IndexType index)
  {
    if (this->count > 1)
      (*this)[index] = (*this)[count - 1];
    this->count--;
  }

  inline void clear() { this->count = 0; }
  inline IndexType size() const { return this->count; }
  inline bool empty() const { return this->count == 0; }
  inline bool full() const { return this->count == MaximumSize; }
  inline const T& back() const { return (*this)[this->count - 1]; }
  inline T& back() { return (*this)[this->count - 1]; }

  inline typename boost::array<T, MaximumSize>::iterator  end()       { return this->elems + count; }
  inline typename boost::array<T, MaximumSize>::const_iterator  end() const { return this->elems + count; }
  inline typename boost::array<T, MaximumSize>::const_iterator cend() const { return this->elems + count; }
};

【讨论】:

    【解决方案2】:

    您说“它必须是 POD 数据类型”。您的集合不需要是 POD 吗?

    如果数据类型是 POD,则 std::vector 提供您需要的功能,并且许多实现将优化数据为 POD 的复制/移动语义。

    为什么需要替换矢量?

    要直接回答这个问题:我怀疑 boost 中是否存在这样的类,因为 vector(保持大小)已经达到了目的,因此他们认为没有真正的需要。 boost::array 没有不同大小和容量的概念。

    您可以轻松地围绕 vector 或 boost::array 编写自己的包装类,以便在超出您设置的容量时抛出。

    现在,如果您要实现这一点,最可能的方法是使用一个包含向量、boost 数组或常规数组的类,然后实现您的所有函数。我会说这很直观,但可能有一个更聪明的解决方案来重载分配器,而不是在内部保存一个 boost::array 并使用它来进行向量的“分配”。如果您的分配器容量不足(因为其内部数组已满),您将抛出(可能应该是 bad_alloc)。然后,用户只需将该类用作向量并可以调用其所有函数,但如果他们试图将向量增大到高于其大小,您将抛出异常。

    由于向量的性质,您的分配器不应作为释放某些对象的堆工作。您只需维护一个连续的缓冲区。

    关于 POD 或近似 POD 问题,向量中的实际数据保证是一个连续的缓冲区,并且您的分配器在内部使用 boost::array 所以它将是连续的。如果你想要序列化的东西,那么 boost::serialize 已经可以正常使用向量了。

    整个集合、容器和 内容,必须是单个块 固定大小的内存:确定在 编译时

    您的分配器可能如下所示:

    typename< typename T, size_t SIZE >
    class Allocator
    {
        struct Data
        {
            size_t num_used;
            T[SIZE] data;
        } d;
    
    public:
        // implement allocator's methods
    };
    

    实现these

    【讨论】:

    • @CashCow:我认为这是因为他想确保永远不会超过某个最大尺寸。
    • 他想要某种尺寸有限的容器,所以你用 push_back() 插入,但装满时失败?
    • 这就是我对问题的理解:显然push_back() 如果超出容量,则必须通过异常处理。
    • push_back()、insert() 或任何其他可能增加集合的函数都需要检查容量,包括采用 2 个迭代器的构造函数
    • 整个集合、容器和内容,必须是固定大小的单个内存块:在编译时确定。
    【解决方案3】:

    另一种选择(如果您不介意 RYO)是使用 boost::optional&lt;T&gt; 作为数组,例如:

    boost::array<boost::optional<int>, 10>
    

    上面的好处是你可以测试每个条目以查看它是否已设置,而不是使用一些值状态来进行未初始化。这仍然应该允许对象的直接内存复制(无论是堆栈还是动态分配)。

    【讨论】:

    • sizeof(boost::optional&lt;int&gt;) > sizeof(int),而boost::optional&lt;&gt; 不是 POD 类型,所以我不明白这如何允许 memcpy 序列化。
    • @ildjarn,在这种情况下,大小并没有多大意义,boost::optional 不是 POD 的事实也没有多大意义,重要的是 boost::optional 在内部没有额外的动态内存分配(或虚函数等),因此以这种方式复制是安全的。
    【解决方案4】:

    我所知道的更接近的是llvm::SmallVector&lt;T,N&gt;,但它稍微复杂一些,因为它在大小超过N 时使用动态分配的存储(它还复制现有项目,因为存储保证是连续的) .

    我认为实现这样的容器并不难,尤其是使用boost::array 作为后端。一个简单的包装器来保持大小和移动项目(例如从vector 的代码中获得灵感)就足够了。

    【讨论】:

      【解决方案5】:

      问题是(可能)初始化。你如何初始化 大小,如果是 POD?你如何强制执行不变量 size()

      从技术上讲,定义一些东西很容易,但是否 它是否有用或安全是另一个问题。你为什么不 只需使用 boost::array,并将当前大小保持为 一个单独的变量?

      -- 詹姆斯·坎泽

      【讨论】:

      • @edA-qa mort-ora-y:具有构造函数的类不是 POD。时期。如果您不信任任何人,请在书中查找。
      • 假设我调用了一个init 函数。
      • 显然在 C++0x 中,POD 的定义被放宽到包含带有构造函数的类。
      • @edA-qa mort-ora-y:如果问题是关于 C++0x 的,那么值得在问题中提及并适当地标记它。 C++0x (还)不是标准。
      • @Steve,不,我很好被纠正了,我把所有东西都改成了类似 POD 的。只是觉得他们改变了 0x 中的含义很有趣(不过我不确定它是否涵盖了我想要的内容)。
      【解决方案6】:

      POD 代表普通旧数据。 “C++ 中的普通旧数据结构是一个聚合类,它只包含 PODS 作为成员,没有用户定义的析构函数,没有用户定义的复制赋值运算符,也没有指向成员类型的非静态成员。”基本上根据定义,任何可以在运行时调整大小的东西都不能是 POD。

      【讨论】:

      • 一般来说,如果是 POD,它可以安全地逐字节复制,尽管如果你的数据包含指针并且你移动数据,你的指针可能仍然指向旧位置,所以不能保证为了安全起见,例如在 C 代码中
      • 我并不是说我需要动态内存分配。 POD 维护状态信息没有问题,与它们的定义不冲突。
      猜你喜欢
      • 1970-01-01
      • 2011-02-14
      • 2019-11-13
      • 1970-01-01
      • 2014-03-20
      • 1970-01-01
      • 2021-05-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多