使用 std::vector 作为原始内存的视图

新手上路,请多包涵

我正在使用一个外部库,它在某些时候给了我一个指向整数数组和大小的原始指针。

现在我想使用 std::vector 来访问和修改这些值,而不是使用原始指针访问它们。

这是一个解释这一点的人工示例:

 size_t size = 0;
int * data = get_data_from_library(size);   // raw data from library {5,3,2,1,4}, size gets filled in

std::vector<int> v = ????;                  // pseudo vector to be used to access the raw data

std::sort(v.begin(), v.end());              // sort raw data in place

for (int i = 0; i < 5; i++)
{
  std::cout << data[i] << "\n";             // display sorted raw data
}

预期输出:

 1
2
3
4
5

原因是我需要在该数据上应用来自 <algorithm> 的算法(排序、交换元素等)。

另一方面,改变该向量的大小永远不会改变,因此 push_backeraseinsert 不需要在该向量上工作。

我可以根据库中的数据构造一个向量,使用修改该向量并将数据复制回库,但这将是我想避免的两个完整副本,因为数据集可能非常大。

原文由 Jabberwocky 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 577
2 个回答

问题是 std::vector 必须从您初始化它的数组中复制元素,因为它拥有它包含的对象的所有权。

为避免这种情况,您可以对数组使用 切片 对象(即,类似于 std::string_viewstd::string 的含义)。您可以编写自己的 array_view 类模板实现,其实例是通过将原始指针指向数组的第一个元素和数组长度来构造的:

 #include <cstdint>

template<typename T>
class array_view {
   T* ptr_;
   std::size_t len_;
public:
   array_view(T* ptr, std::size_t len) noexcept: ptr_{ptr}, len_{len} {}

   T& operator[](int i) noexcept { return ptr_[i]; }
   T const& operator[](int i) const noexcept { return ptr_[i]; }
   auto size() const noexcept { return len_; }

   auto begin() noexcept { return ptr_; }
   auto end() noexcept { return ptr_ + len_; }
};

array_view 不存储数组;它只保存一个指向数组开头的指针和该数组的长度。因此, array_view 对象的构造和复制成本很低。

Since array_view provides the begin() and end() member functions, you can use the standard library algorithms (eg, std::sort , std::find , std::lower_bound 等)就可以了:

 #define LEN 5

auto main() -> int {
   int arr[LEN] = {4, 5, 1, 2, 3};

   array_view<int> av(arr, LEN);

   std::sort(av.begin(), av.end());

   for (auto const& val: av)
      std::cout << val << ' ';
   std::cout << '\n';
}

输出:

 1 2 3 4 5


使用 std::span (或 gsl::span )代替

上面的实现暴露了 切片对象 背后的概念。但是,从 C++20 开始,您可以直接使用 std::span 代替。在任何情况下,您都可以使用 gsl::span 从 C++14 开始。

原文由 ローウ 发布,翻译遵循 CC BY-SA 4.0 许可协议

C++20’s std::span

如果您能够使用 C++20,则可以使用 std::span 这是一个指针 - 长度对,可以让用户查看连续的元素序列。 It is some sort of a std::string_view , and while both std::span and std::string_view are non-owning views, std::string_view is a read-only看法。

从文档:

类模板 span 描述了一个对象,该对象可以引用一个连续的对象序列,该序列的第一个元素位于零位置。跨度可以具有静态范围,在这种情况下,序列中的元素数量是已知的并以类型编码,也可以具有动态范围。

因此,以下将起作用:

 #include <span>
#include <iostream>
#include <algorithm>

int main() {
    int data[] = { 5, 3, 2, 1, 4 };
    std::span<int> s{data, 5};

    std::sort(s.begin(), s.end());

    for (auto const i : s) {
        std::cout << i << "\n";
    }

    return 0;
}

现场,l:‘5’,n:‘0’,o:‘C%2B%2B+source+%231’,t:‘0’)),k:49.85494633014216,l:‘4’,n:‘0’,o:“,s:0,t:‘0’),(g:!((g:!((h:compiler,i:(compiler:gsnapshot,filters:(b:‘0’,binary:‘1’,commentOnly:‘0’,demangle:‘0’,directives:‘0’,execute:‘0’,intel:‘0’,libraryCode:‘1’,trim:‘1’),fontScale:14,j:2,lang:c%2B%2B,libs:!(),options:‘-std%3Dc%2B%2B2a’,selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:‘5’,n:‘0’,o:‘x86-64+gcc+(trunk)+(Editor+%231,+Compiler+%232)+C%2B%2B’,t:‘0’)),k:50.145053669857845,l:‘4’,m:50,n:‘0’,o:”,s:0,t:‘0’),(g:!((h:output,i:(compiler:2,editor:1,fontScale:14,wrap:‘1’),l:‘5’,n:‘0’,o:‘%232+with+x86-64+gcc+(trunk)’,t:‘0’)),header:(),l:‘4’,m:50,n:‘0’,o:“,s:0,t:‘0’)),k:50.145053669857845,l:‘3’,n:‘0’,o:”,t:‘0’)),l:‘2’,n:‘0’,o:“,t:‘0’)),version:4) 查看

由于 std::span 基本上是指针 - 长度对,您也可以按以下方式使用:

 size_t size = 0;
int *data = get_data_from_library(size);
std::span<int> s{data, size};

注意: 并非所有编译器都支持 std::span在此处 检查编译器支持。

更新

如果你不能使用 C++20,你可以使用 gsl::span 这基本上是 C++ 标准的基本版本 std::span

C++11解决方案

如果您仅限于 C++11 标准,您可以尝试实现自己的简单 span 类:

 template<typename T>
class span {
   T* ptr_;
   std::size_t len_;

public:
    span(T* ptr, std::size_t len) noexcept
        : ptr_{ptr}, len_{len}
    {}

    T& operator[](int i) noexcept {
        return *ptr_[i];
    }

    T const& operator[](int i) const noexcept {
        return *ptr_[i];
    }

    std::size_t size() const noexcept {
        return len_;
    }

    T* begin() noexcept {
        return ptr_;
    }

    T* end() noexcept {
        return ptr_ + len_;
    }
};

现场 查看 C++11 版本

原文由 NutCracker 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题