1. ホーム
  2. c++

[解決済み] std::vectorを生メモリへのビューとして使用する。

2023-03-03 10:19:40

質問

外部ライブラリを使っているのですが、ある時点で整数の配列への生ポインタとサイズが渡されます。

ここで、私は 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_back , erase , insert はそのベクターで動作する必要はありません。

ライブラリからのデータに基づいてベクトルを構築し、そのベクトルを修正してライブラリにデータをコピーして使用することもできますが、データセットが非常に大きくなる可能性があるため、完全なコピーを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 オブジェクトは構築するのもコピーするのも簡単です。

から array_viewbegin()end() のメンバ関数を使用する場合は、標準ライブラリのアルゴリズム(例えば 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 以降で使用することができます。