Using std::vector as view on to raw memory
C++20's std::span
If you are able to use C++20, you could use std::span
which is a pointer - length pair that gives the user a view into a contiguous sequence of elements. 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 view.
From the docs:
The class template span describes an object that can refer to a contiguous sequence of objects with the first element of the sequence at position zero. A span can either have a static extent, in which case the number of elements in the sequence is known and encoded in the type, or a dynamic extent.
So the following would work:
#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;
}
Check it out live
Since std::span
is basically pointer - length pair, you can use in a following manner too:
size_t size = 0;
int *data = get_data_from_library(size);
std::span<int> s{data, size};
Note: Not all compilers support std::span
. Check compiler support here.
UPDATE
If you are not able to use C++20, you could use gsl::span
which is basically the base version of the C++ standard's std::span
.
C++11 solution
If you are limited to C++11 standard, you can try implementing your own simple span
class:
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_;
}
};
Check out C++11 version live
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.