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.