我需要 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
上,元素改组(由于insert
或erase
)可能是最引人注目的例子。
另请注意,使用 C++0x 和初始化列表 vector
获得 emplace_back
直接在适当位置构造元素,从而提升 CopyConstructible
要求,这可能是一个不错的好处,具体取决于您的情况。
【讨论】:
我将其标记为已回答,但我真的无法确定这种方法是否存在任何潜在问题(除了明显的 std::aligned_storage 可能尚不被所有编译器支持) .【参考方案2】:boost::array<T, 12> ta;
与T[12] ta;
没有区别;如果你不使用初始化列表,那么元素将被默认构造。
常见的解决方法是boost::array<T*, 12> ta;
或boost::array<unique_ptr<T>, 12> 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<type, size>
(可能与 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】:
为什么它必须驻留在堆栈中?您是否有经验证据表明创建和reserve
vector
太慢(使用vector
似乎是显而易见的答案)?
即使是这样,您也可以创建一个具有保留空间的向量池,并将 swap
预分配的向量之一创建到本地副本中。当你用完本地的后,再把它换回来(很像list
s 的splice
技巧)。
【讨论】:
我没有投反对票,但在我看来,这将是一个很好的评论候选人。以上是关于我需要 C++ 数组类模板,它是固定大小、基于堆栈且不需要默认构造函数的主要内容,如果未能解决你的问题,请参考以下文章