I'm using a external library which at some point gives me a raw pointer to an array of integers and a size.
Now I'd like to use std::vector
to access and modify these values in place, rather than accessing them with raw pointers.
Here is an articifial example that explains the point:
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
}
Expected output:
1
2
3
4
5
The reason is that I need to apply algorithms from <algorithm>
(sorting, swaping elements etc.) on that data.
On the other hand changing the size of that vector would never be changed, so push_back
, erase
, insert
are not required to work on that vector.
I could construct a vector based on the data from the library, use modify that vector and copying the data back to to library, but that would be two complete copies that I'd like to avoid as the data set could be really big.
The problem is that std::vector
has to make a copy of the elements from the array you initialize it with as it has the ownership of the objects it contains.
To avoid this, you can use a slice object for an array (i.e., similar to what std::string_view
is to std::string
). You could write your own array_view
class template implementation whose instances are constructed by taking a raw pointer to an array's first element and the array length:
#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
doesn't store an array; it just holds a pointer to the beginning of the array and the length of that array. Therefore, array_view
objects are cheap to construct and to copy.
Since array_view
provides the begin()
and end()
member functions, you can use the standard library algorithms (e.g., std::sort
, std::find
, std::lower_bound
, etc.) on it:
#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';
}
Output:
1 2 3 4 5
Use std::span
(or gsl::span
) instead
The implementation above exposes the concept behind slice objects. However, since C++20 you can directly use std::span
instead. In any case, you can use gsl::span
since C++14.