【问题标题】:Any alternative to std::dynarray presently available?目前有任何替代 std::dynarray 的方法吗?
【发布时间】:2013-06-22 15:18:28
【问题描述】:

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?
  • 动态能力是重量级的,这里不需要。我需要一个固定大小的容器,很像一个普通的旧 C 数组,但附加了一些元数据并能够迭代。
  • 您仍然可以使用 std::vector 和 reserve() 提前您需要的大小。这样您在插入时就不会受到调整大小的影响。
  • 我删除了你的 C++14 标签。 This meta question 达成共识,等到最终标准草案发布后再使用它而不是 C++1y。
  • 在 C++14 存在之前说“C++14 will”的问题在于,C++14 还不存在:-S

标签: c++ c++11 c++14


【解决方案1】:

我认为std::vector 是您在dynarray 可用之前正在寻找的内容。只需使用分配构造函数或reserve,您就可以避免重新分配开销。

【讨论】:

  • dynarray和vector的主要区别是什么?
  • @user814628 vector 创建后可以调整大小,to-be-dynarray 不能。
  • @user814628 这个假设可以更好地优化容器。
  • @user814628:假设库实现能够在std::dynarray的情况下分配堆栈中的元素,这将使其更接近C中的VLA。如果这是情况下,那么dynarray 不会产生分配成本,但实现dynarray 并非易事(至少作为一个库)
  • @DavidRodríguez-dribeas 给定一个\常量数n,我们如何在运行时在堆栈上分配n-elements
【解决方案2】:

如果您不需要std::dynarray&lt;T&gt;::at() 提供的范围检查访问权限,我将投票给std::unique_ptr&lt;short[]&gt;(new short[n])。你甚至可以使用初始化列表:

#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';
}

【讨论】:

  • 但是......你不能使用漂亮的for循环语法!
  • @Yakk True true... ;) 但你可以使用 std::for_each(myarray.get(), myarray.get() + 3, [](short val) { std:: cout
  • @andre 不一样。 嗅探。另外,您必须在某处记住3dynarray 会为您记住。
  • 您应该编写一个make_unique 函数并使用它而不是编写new
【解决方案3】:

您可以(ab)使用std::valarray&lt;short&gt;

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++ 中它被实现为 (length, data) 对,而在 libc++ 中它被实现为 (begin, end)。)

【讨论】:

  • 太棒了! std::valarray 尽可能接近纯数组(不包括 std::dynarray),我对您列出的限制很好(很好!)。
  • 对于使用正确的重载创建的std::vector 并仅使用operator[] 访问元素,您只是在削减一个数据成员(容量)。这闻起来很多对我来说就像过早的优化......
  • 我会说 dynarray 的情况更多的是关于防御性编程而不是优化——它不应该可能调整一个没有调整业务大小的数组。跨度>
  • 为什么是malloc()?为什么不raw_array = new short(12);
  • 对于堆栈分配使用,如果可以接受使用非标准编译器功能,raw_array 可能是 VLA。然后,您的代码可用于实现dynarray,直到它可用为止。
【解决方案4】:

缓冲区和大小,加上一些基本方法,可以满足您的大部分需求。

很多样板,但像这样:

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= 和琐碎的构造函数。

【讨论】:

  • 据我所知 std::dynarray 在堆栈上分配内存,因此可以扩展或修改上面的模板,使其不在堆上分配内存而是使用外部内存它是用 alloc(sizeof(T) * NUMBER_OF_ELEMENTS) 分配的。然后只需在模板'c'tor 内的所有元素上调用就地构造函数,并在销毁时调用d'tor。没查过,不过应该不太难实现
  • @FelixPetriconi alloca(基于堆栈的分配)将难以支持,因为无法从函数返回alloca 的内存!您必须采用“范围” lambda,它是数组在其中有效的“主体”,或者在alloca'd 内存methinks 中传递包含范围。
【解决方案5】:

C++14 还添加了可变长度数组,类似于 C99 中的数组,一些编译器已经支持:

void foo(int n) {
  int data[n];
  // ...
}

它不是容器,因为它不支持begin()end() 等,但可能是一个可行的解决方案。

【讨论】:

  • 它们不被称为可变长度数组,除了声明语法之外,它们与 C99 VLA 并不真正相似。
  • @BenVoigt:他们叫什么?
  • “运行时绑定的数组”,但它们被从 C++14 中拉了出来
【解决方案6】:

dynarray 在没有堆栈分配组件的情况下很容易实现——这显然是不可能的,直到也许 C++14 无论如何——所以我只是滚动了一个dynarray inverse-backport (forwardport?) 作为我的库的一部分并从那时起开始使用它。到目前为止,在 C++03 中工作,没有任何“内布拉斯加州的无效”子句,因为它并不完全依赖于任何 C++11 特定的功能,而且它很整洁
这样,当 C++1y/2z dynarray 出现时,我的代码大部分仍然兼容。

(这也是许多明显的“为什么 C++ 不早点拥有这个?”的事情之一,所以拥有它是件好事)。

这是在我了解到显然 C++1y-dynarray 和 C++1y-runtime-size-arrays 是完全相同的提议(一个只是另一个的语法糖)而不是两个不同的提议之前- 我最初想到的补充建议。因此,如果我现在必须解决同样的问题,我可能会切换到基于 @Yakk 正确性解决方案的东西。

【讨论】:

  • 这应该是问题的答案还是什么?
猜你喜欢
  • 2013-10-07
  • 2020-01-21
  • 1970-01-01
  • 2016-12-10
  • 1970-01-01
  • 2010-12-22
  • 1970-01-01
  • 1970-01-01
  • 2018-10-09
相关资源
最近更新 更多