How to get const references to a range of std::vector elements?
Asked Answered
S

3

7

I want to get a range of elements from a std::vector<MyClass> and store them as const-ref, because I only want to read but not modify them.

#include <iostream>
#include <vector>

// A "large" user class
class MyClass 
{
public:
    int data;
    MyClass(int val) : data(val) {} 
};    

int main() 
{
    // Initialize vector to MyClass objects
    std::vector<MyClass> myClass{1, 2, 3, 4, 5, 6, 7, 8};

    // Get const reference to element with index 0
    const auto &el = myClass[0]; 

    // Get const references to a range of elements
    const unsigned int start = 1, end = 4;
    // const auto &elRange = ???

    return 0;
}

How can I achieve something like this in C++17 or lower?

Sweitzer answered 6/9, 2024 at 7:43 Comment(3)
You can use std::span if you can upgrade to C++20, or simply store the indices in the new vector.Beastly
Sounds like you just want a pair of const_iterators: const auto elRangeBegin = myClass.cbegin() + 1; const auto elRangeEnd = myClass.cbegin() + 4;Gymnasium
What's wrong with std::span<const MyClass>?Banff
C
10

I want to get a range of elements from a std::vector and store them as const references, because I only want to read but not modify them.

The best would be using std::span, which requires support.

Otherwise, you write a simple SimpleSpan class to do so. As simple as:

// A simple span-like class for C++17
template<typename T>
class SimpleSpan 
{
   T* data_;
   std::size_t size_;

public:
   constexpr SimpleSpan(T* data, std::size_t size) : data_{ data }, size_{ size } {}

   constexpr T& operator[](std::size_t index) { return data_[index]; }
   constexpr T& operator[](std::size_t index) const { return data_[index]; }

   constexpr T* begin() { return data_; }
   constexpr T* end() { return data_ + size_; }
   constexpr T* begin() const { return data_; }
   constexpr T* end() const { return data_ + size_; }

   constexpr std::size_t size() const { return size_; }
};

and just

// Get const references to a range of elements using our SimpleSpan
const unsigned int start = 1, end = 4;
SimpleSpan<const MyClass> elRange(&myClass[start], end - start);

See live demo

Carlie answered 6/9, 2024 at 7:55 Comment(4)
Do we not have to write a destructor of this class? Since SimpleSpan has a raw pointer as memberSweitzer
@Sweitzer the class does not own or manage the data. What would you put in your suggested destructor ?Beastly
@Sweitzer The std::span/ SimpleSpan is a non-owning view of a contiguous sequence of objects. It doesn't manage any resources directly. It designed to be simple and lightweight, avoiding the need for custom resource management. Therefore, no need for rule of 3/5!Carlie
And that's the thing with references/pointers/views on vectors. You should not store them anywhere (just use them in local scope). One modification to the underlying vector can reallocate and then your view is no longer valid.Quite
L
6

That's what std::span is made for, but you will need C++20 for it.

If you are stuck on lesser C++ versions you can use boost::span. (It needs 2 headers to include in a project, you don't need the entire boost library)

auto elements = boost::span<const MyClass>{myClass}.subspan(start, end-start);

C++17 godbolt example

Lunnete answered 6/9, 2024 at 7:51 Comment(0)
G
0

std::span

const auto elRange = std::span(&myClass[1], &myClass[4]);
Gidgetgie answered 6/9, 2024 at 7:47 Comment(3)
I forgot to mention. I only have c++17Sweitzer
Write own view class of you don't want use indexes and the arithmetic.Gidgetgie
@Sweitzer then edit your question and state this clearly there so everyone would notice that.Berkeley

© 2022 - 2025 — McMap. All rights reserved.