从原始数据就地创建 std::vector

Create std::vector in-place from raw data

本文关键字:std vector 创建 原始数据      更新时间:2023-10-16

给定一个原始元素数组,如何创建一个在不重新分配和复制的情况下获得原始数组所有权的std::vector

例如,具有原始数组:

int* elems = new int[33]

如何创建指向elems的大小33std::vector

我确信从理论上讲这是可能的,因为通常std::vector被实现为包含三个指针的结构,一个指向分配内存的开头,一个指向有效元素的末尾,一个指向分配内存的末尾。但是有没有使用原始数组初始化std::vector结构的标准方法?

你需要的是一个"视图"而不是一个容器。容器拥有自己的元素,它们的主要目的是封装它们管理的原始内存。如果您需要自己管理内存,则不需要容器。看看如果你有string,那将是你的解决方案string_view。也许您可以应用增强范围。来自文档(强调我的):

范围概念的动机是有许多有用的 不满足 容器,以及许多可以用这个减少的算法来编写 一组要求。特别是,范围不一定

  • 拥有可以通过它访问的元素
  • 具有复制语义,

PS:实际上std::array_view被考虑为C++17,但不幸的是它没有进入标准。

这不能直接实现的原因是标准库使用分配器为容器保留内存。

因此,如果你有一个使用某种类型的分配器的std::vector,并给它一些你创建的指针,你有效地打破了分配器习惯用法。例如,如果你的标准库的实现使用mallocfree而不是newdelete,你的程序将失败。

要使这成为一种标准方式,标准库需要提供一个接受T*的构造函数,该该必须由向量稍后使用的同一分配器返回。因此,您需要的构造函数的签名类似于std::vector::vector(T* data, size_type used_elements, size_type capacity, const Allocator& alloc).请注意,分配器参数是必需的,因为T*必须(理论上)由向量中使用的完全相同的分配器返回。


你可以通过根据这个概念创建自己的分配器来实现一些功能,但是要让你的33元素不被重建,你还必须提供一个allocator::construct(..)函数,这是一个无操作的函数,直到34th元素(独占)。此外,您还必须首先将矢量大小调整为33元素,以强制矢量具有正确的大小。

话虽如此,这仍然是一个坏主意,因为对于条件构造和分配函数,您可能会有更多的开销,而不是复制一次元素。

根据这一点,没有接受指向数据的指针的构造函数。因此,您不能将原始数组的所有权传递给矢量。

您只能创建矢量并将数据放入其中。

如果您正在处理的对象类型是可移动的,则可以执行以下操作:

template<typename T>
std::vector<std::unique_ptr<T>> ConvertArrayToVector(T* data, size_t size)
{
std::vector<std::unique_ptr<T>> result(size);
for (unsigned int i = 0; i<size; ++i)
result[i] = std::make_unique<T>(std::forward<T>(data[i]));
return result;
}

生成的向量现在拥有数组,从某种意义上说,它存储指向其元素的指针,并确保在销毁向量时删除对象,但原始数组在此过程中失效。

给定一个原始的元素数组,如何创建一个std::vector在不重新分配和复制的情况下获得原始数组的所有权?

没有办法。

如何创建大小为 33 的指向 ELEMS 的std::vector

不可能的。

我相信从理论上讲这是可能的,

不,不是。

但是有没有使用原始数组初始化std::vector结构的标准方法?

不。


话虽如此,您有可能使用自定义分配器将解决方案组合在一起。但是,除了编写自定义分配器是一种很少使用且容易出错的技术这一事实之外,您不应高估此类解决方案的可用性。

std::vector<int>std::vector<int, MyAllocator>两个不同的类。如果您的目标是与需要std::vector<int>的代码进行交互,则不能使用std::vector<int, MyAllocator>;如果您打算在代码中创建和使用std::vector<int, MyAllocator>,那么老实说,您最好只实现自己的非拥有容器类,即类似于自定义VectorView<T>的东西。