C++11 给了我们很棒的
std::array
,这需要在编译时知道大小:
std::array<int, 3> myarray = {1, 2, 3};
现在,我碰巧有一些旧的
short*
缓冲区需要包装,其大小仅在运行时才知道(当然,它会是)。
C++14 将定义
std::dynarray
来涵盖这种情况,但 dynarray
在 GCC 4.7 和 Clang 3.2 中尚不可用。
那么,有谁知道有一个容器可以与
std::array
(就效率而言)相当,但不需要在编译时指定大小?我怀疑 Boost 已经为我准备好了一些东西,尽管我找不到任何东西。
我认为
std::vector
是您在 dynarray
可用之前所寻找的。只需使用分配构造函数或 reserve
即可避免重新分配开销。
如果您不需要
std::unique_ptr<short[]>(new short[n])
提供的范围检查访问权限,我会投票给 std::dynarray<T>::at()
。您甚至可以使用初始化列表:
#include <iostream>
#include <memory>
int main(int argc, char** argv) {
const size_t n = 3;
std::unique_ptr<short[]> myarray(new short[n]{ 1, 2, 3 });
for (size_t i = 0; i < n; ++i)
std::cout << myarray[i] << '\n';
}
您可以(ab)使用
std::valarray<short>
。
int main() {
short* raw_array = (short*) malloc(12 * sizeof(short));
size_t length = 12;
for (size_t i = 0; i < length; ++ i) {
raw_array[i] = (short) i;
}
// ...
std::valarray<short> dyn_array (raw_array, length);
for (short elem : dyn_array) {
std::cout << elem << std::endl;
}
// ...
free(raw_array);
}
valarray
支持 dynarray
的大多数功能,除了:
.at()
.data()
请注意,标准(从 n3690 开始)不要求
valarray
存储是连续的,尽管没有理由不这样做:)。
(对于一些实现细节,在 libstdc++ 中它被实现为(长度,数据)对,而在 libc++ 中它被实现为(开始,结束)。)
一个缓冲区和一个大小,再加上一些基本方法,就可以给你大部分你想要的东西。
很多样板文件,但类似这样:
template<typename T>
struct fixed_buffer {
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* iterator;
typedef const T* const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
std::size_t length;
std::unique_ptr<T[]> buffer;
std::size_t size() const { return length; }
iterator begin() { return data(); }
const_iterator begin() const { return data(); }
const_iterator cbegin() const { return data(); }
iterator end() { return data()+size(); }
const_iterator end() const { return data()+size(); }
const_iterator cend() const { return data()+size(); }
reverse_iterator rbegin() { return {end()}; }
const_reverse_iterator rbegin() const { return {end()}; }
const_reverse_iterator crbegin() const { return {end()}; }
reverse_iterator rend() { return {begin()}; }
const_reverse_iterator rend() const { return {begin()}; }
const_reverse_iterator crend() const { return {begin()}; }
T& front() { return *begin(); }
T const& front() const { return *begin(); }
T& back() { return *(begin()+size()-1); }
T const& back() const { return *(begin()+size()-1); }
T* data() { return buffer.get(); }
T const* data() const { return buffer.get(); }
T& operator[]( std::size_t i ) { return data()[i]; }
T const& operator[]( std::size_t i ) const { return data()[i]; }
fixed_buffer& operator=(fixed_buffer &&) = default;
fixed_buffer(fixed_buffer &&) = default;
explicit fixed_buffer(std::size_t N):length(N), buffer( new T[length] ) {}
fixed_buffer():length(0), buffer() {}
fixed_buffer(fixed_buffer const& o):length(o.N), buffer( new T[length] )
{
std::copy( o.begin(), o.end(), begin() );
}
fixed_buffer& operator=(fixed_buffer const& o)
{
std::unique_ptr<T[]> tmp( new T[o.length] );
std::copy( o.begin(), o.end(), tmp.get() );
length = o.length;
buffer = std::move(tmp);
return *this;
}
};
at()
缺失,分配器也缺失。
operator=
与 dyn_array
提案不同——提案块 operator=
,我赋予它值语义。有一些方法效率较低(例如 copy
构造)。我允许空fixed_buffer
。
这可能会阻止使用堆栈来存储
dyn_array
,这可能就是它不允许这样做的原因。如果您想要更接近 dyn_array 的行为,只需删除我的 operator=
和简单的构造函数即可。
C++14 还添加了可变长度数组,类似于 C99 中的数组,并且一些编译器已经支持了:
void foo(int n) {
int data[n];
// ...
}
它不是一个容器,因为它不支持
begin()
和 end()
等,但可能是一个可行的解决方案。
dynarray
很容易在没有堆栈分配组件的情况下实现自己 - 这显然在 也许 C++14 之前是不可能做到的 - 所以我只是推出了 dynarray
反向向后移植(前向移植?)作为我图书馆的一部分,从那时起就开始使用它。到目前为止,在 C++03 中工作,没有任何“内布拉斯加州无效”子句,因为它并不绝对依赖于任何 C++11 特定的功能,并且拥有 dynarray
出现时,我的代码在很大程度上仍然是兼容的。
(这也是许多明显的“为什么 C++ 不早点拥有这个?”的事情之一,所以拥有它是件好事)。
这是在我了解到显然 C++1y-
dynarray
和 C++1y-runtime-size-arrays 是完全相同的提案(一个只是另一个的语法糖)而不是两个不同但互补的提案之前正如我首先想到的那样。因此,如果我现在必须解决同样的问题,我会可能切换到基于@Yakk解决方案的正确性。
如果您熟悉 C++17,这个 2021 年的仅标头库似乎是一个不错的选择:https://github.com/cnbatch/dynarray