我需要 C++ 数组类模板,它是固定大小、基于堆栈且不需要默认构造函数

Posted

技术标签:

【中文标题】我需要 C++ 数组类模板,它是固定大小、基于堆栈且不需要默认构造函数【英文标题】:I need C++ array class template, which is fixed-size, stack-based and doesn't require default constructor 【发布时间】:2010-10-06 14:35:16 【问题描述】:

所以,我一直在研究 boost::array 但它确实需要定义默认构造函数。 我认为用数据填充这个数组的最好方法是通过 push_back(const T&) 方法。调用它的次数超过 SIZE(在编译时已知)会导致断言或异常,具体取决于构建配置。这样,它将始终包含有意义的数据。 有谁知道这个概念的高效、便携、可靠的实现?

【问题讨论】:

push_back 与“固定大小”相矛盾。我想我知道你的意思,它的大小有一个固定的上限,而且一旦你填充了它,你就不会改变大小。我不知道它的实现。 “不需要默认构造函数”是什么意思? char array[5] 是固定大小的,可以模板化,并且是基于堆栈的。奖励:它甚至可以在C 中使用。 :D @Steve:我认为他的真正意思是他想要一个“固定容量”。 @user467799:猜boost::array<T*> 不满足你? 【参考方案1】:

好吧,我以为现在有人会带来答案,但似乎没有,所以我们走吧。

你想要的是我自己梦寐以求的东西:boost::optional_array<T,N>

有两种变体:

首先:类似于boost::array< boost::optional<T>, N >,即每个元素都可以设置也可以不设置。 第二个:类似于std::vector<T>(不知何故),即所有开始元素均已设置,而所有后续元素均未设置。

鉴于之前的问题/cmets,您似乎想要第二个,但这并不重要,因为两者非常相似。

template <typename T, size_t N>
class stack_vector

public:
  bool empty() const  return mSize == 0; 
  size_t size() const  return mSize; 
  size_t capacity() const  return N; 
  size_t max_size() const  return N; 

  T& operator[](size_t i)  return *(this->pfront() + i); 
  /// ...

private:
  T* pfront() const  return reinterpret_cast<T*>(&mStorage); 

  std::aligned_storage< N * sizeof(T), alignof(T) > mStorage;
  size_t mSize; // indicate how many elements are set, from the beginning
;

让我们专注于那些非常特殊的操作:

template <typename T, size_t N>
void push_back(T const& t)

  new (this->pfront() + mSize) T(t); // in place construction
  ++mSize;


template <typename T, size_t N>
void clear()

  for (size_t i = 0; i != mSize; ++i)
  
    (this->pfront() + i)->~T();
  
  mSize = 0;

如您所见,主要的困难是要记住:

如果那里还没有构建任何元素,则需要放置 new + 复制构造而不是赋值。 应该正确处理“过时”的元素(即在最后一个元素之后)(即调用它们的析构函数)。

传统 STL 容器上有许多操作可能难以实现。在vector 上,元素改组(由于inserterase)可能是最引人注目的例子。

另请注意,使用 C++0x 和初始化列表 vector 获得 emplace_back 直接在适当位置构造元素,从而提升 CopyConstructible 要求,这可能是一个不错的好处,具体取决于您的情况。

【讨论】:

我将其标记为已回答,但我真的无法确定这种方法是否存在任何潜在问题(除了明显的 std::aligned_storage 可能尚不被所有编译器支持) .【参考方案2】:

boost::array&lt;T, 12&gt; ta;T[12] ta; 没有区别;如果你不使用初始化列表,那么元素将被默认构造。

常见的解决方法是boost::array&lt;T*, 12&gt; ta;boost::array&lt;unique_ptr&lt;T&gt;, 12&gt; ta;

按值存储的唯一方法是复制,没有办法……这就是初始化列表的作用:

struct A 
    A(int i):_i(i) cout << "A(int)" << endl; 
    A(const A& a) cout << "A(const A&)" << endl; 
    ~A() cout << "~A()" << endl; 

    int _i;
;

int main()
    boost::array<A, 2> ta = 1, 2;

这个输出:

A(int)
A(const A&)
A(int)
A(const A&)
~A()
~A()
~A()
~A()

http://codepad.org/vJgeQWk5

【讨论】:

不,这不符合我的需求。我想按值而不是按地址将几个小对象存储在小数组中。 在某些情况下使用初始化列表可能很麻烦。您可以通过在一个地方更改编译时常量来轻松更改数组大小,但可能您必须在许多其他地方更改初始值设定项列表。 我并不是说它不会很麻烦,我只是说这是做你想做的事情的唯一方法(除非另一个答案中推荐的 opaque type hack)。【参考方案3】:

可以在你的 boost::array 中存储一个 boost::variant?将第一个参数设置为 int 或其他东西..

boost::array<boost::variant<int, foo>, 6> bar;

好的,你必须处理一个变体,但它是堆栈分配的......

【讨论】:

这是一个解决方案,但远非最佳。我猜 boost::optional 类证明了我想要的东西是可行的 - 可能 boost::optional 实现可以扩展到数组。 直到现在才意识到这一点,只是阅读它......整洁! thx 的指针。比上面的更干净... @user467799:当然,这是可行的。但是您可能必须自己编写这样的课程。注意对齐问题。【参考方案4】:

在 C++0x 中,你得到了 std::array&lt;type, size&gt;(可能与 boost::array 相同)。可以使用fill()std::fill_n()初始化数组数据:

std::array<int, 30> array;
array.fill(0);
boost::array<int, 30> barray;
std::fill_n(barray.begin(), 30, 0);

如果您想在定义时对其进行默认初始化,您可以使用 copy-ctor:

static std::array<int, 30> const nullarray = 0, 0, 0, ..., 0; // nullarray.fill(0);
// (...)
std::array<int, 30> arraynullarray;

【讨论】:

如果您使用用户定义的类型而不是 int,那么所有 30 都将被默认构造(使用 fill 时),这是 OP 不想要的。 @joshperry: 哦,这就是doesn't require default constructor 的意思...使用 sfinae 进行 default-ctor-check【参考方案5】:

为什么它必须驻留在堆栈中?您是否有经验证据表明创建和reservevector 太慢(使用vector 似乎是显而易见的答案)?

即使是这样,您也可以创建一个具有保留空间的向量池,并将 swap 预分配的向量之一创建到本地副本中。当你用完本地的后,再把它换回来(很像lists 的splice 技巧)。

【讨论】:

我没有投反对票,但在我看来,这将是一个很好的评论候选人。

以上是关于我需要 C++ 数组类模板,它是固定大小、基于堆栈且不需要默认构造函数的主要内容,如果未能解决你的问题,请参考以下文章

❥关于C++之模板类vectorarray VS 数组

模板参数固定的C++函数参数数量[重复]

使用向量类实现堆栈的链表与动态数组

堆栈动态和堆栈动态数组

C++在类中定义vector并初始大小的问题

C++ 固定数量的向量大小作为类成员