2

I am currently utilizing the range constructor of std::vector to create another vector of a given subrange.

std::vector<int> myVect { 1, 2, 3, 4 };

std::vector<int> subrangeVector(myVect.begin(), myVect.begin() + 2);

However, this results in the ranged values of myVect being copied and taking up additional memory. This is something that is undesirable when working withing limited memory and/or with very large element types.

How do I construct a subrange of another vector by reference?

A simplified explanation of my objective is as follows:

void fun(std::vector<int> & v) { v.at(0) = 1; }

int main()
{
    std::vector<int> myVect { 1, 2, 3, 4 };

    std::size_t upperLimit = 5;
    std::vector<int> subrangeVector = subrangeView(myVect, upperLimit);

    fun(subrangeVector);  // so myVect.at(0) == 1

    return 0;
}

This would be implemented in many different functions that utilize std::vector as a parameter. I do not want to pass iterators as discussed here. Assume that I have no control over the function fun.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
patyx
  • 324
  • 1
  • 3
  • 17
  • 2
    Why do you want to do this? This is the purpose of iterators to begin with. Anywhere you want to pass your new "subrange reference vector," you could accomplish the same thing passing two iterators. Or if you want to treat it like a vector, you could make a wrapper class for the iterators with all your nice vector functions. – scohe001 May 17 '19 at 16:45
  • 4
    You cannot - a `std::vector` always owns its arguments. However, `std::span` and `std::view::counted` in C++20 provide standard non-owning views. – Brian Bi May 17 '19 at 16:46
  • 1
    @Brian You should write that as an answer, after a few experiments using `std::reference_wrapper` I am convinced and see that it's not possible with the current standard in a simple way. At least it may be possible to take references item by item in a loop. – πάντα ῥεῖ May 17 '19 at 16:53
  • @scohe001 My question is independent from my objective, and as such not relevant to the post. – patyx May 17 '19 at 16:53
  • 1
    @paddycakes _"My question is independent from my objective, and as such not relevant to the post."_ asking about your use cases in a comment is valid, and considered relevant. Your comment sounds unfriendly, keep it civil here please. – πάντα ῥεῖ May 17 '19 at 16:56
  • _@paddycakes_, @Brian Even [my last idea didn't work](http://coliru.stacked-crooked.com/a/0a526260a9a1f2cf), because you can't use `std::reference_wrapper` to change the underlying reference value. The question about the actual use case becomes more and more relevant now. – πάντα ῥεῖ May 17 '19 at 17:07
  • @πάνταῥεῖ Added a simplified use case as requested. – patyx May 17 '19 at 17:11
  • @paddycakes _"Added a simplified use case as requested."_ Then the proposed duplicate may contain the answer you're looking for. I am pretty sure it's not possible simply to use the constructor of `subrangeVector`. You'll need to tie these references being changable. The probably easiest solution for the problem is to use a pair of input iterators, instead of storing references in `subrangeVector`. – πάντα ῥεῖ May 17 '19 at 17:13
  • @πάνταῥεῖ The duplicate is not suitable as I do not want to pass iterators. Assume that the function being called takes as a parameter `std::vector &` and cannot be altered. – patyx May 17 '19 at 17:25
  • 2
    @paddycakes _as I do not want to pass iterators_ Yeah, I assumed so. You probably want to save efficiency regarding to avoid copies. Though there doesn't seem to be a way without accessing the iterators directly. – πάντα ῥεῖ May 17 '19 at 17:29
  • To avoid copies, you could construct a `std::vector>` – Jesper Juhl May 17 '19 at 17:58

1 Answers1

5

A C++ vector is a type which "owns" its memory - it cannot be a "reference type" into another vector's data.

Instead, you will likely find a span useful: A span is a class representing contiguous data in memory - just like a vector; but - it is a "reference type", and it is not owning - just what you wanted to have. It behaves like a vector w.r.t. iteration, operator[], and so on.

In your case, you would write:

std::vector<int> myVect { 1, 2, 3, 4 };
auto subrange { std::span{myVect}.subspan(0, 2); }

and then use subrange just as you were planning to with the vector.

PS: This is C++20 code, since C++17 doesn't have spans yet; if you're using earlier C++ versions, use gsl::span from the Guidelines Support Library (e.g. from here).

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • "it cannot be a "reference type" into another vector's data" - well, if it's a `std::vector` of `std::reference_wrapper` it kind-of *can*. – Jesper Juhl May 17 '19 at 17:59
  • 2
    @JesperJuhl: 1. You're splitting hairs. 2. The data would still be the reference wrappers, not the object-of-reference. – einpoklum May 17 '19 at 18:15
  • I know. But it seemed relevant to mention. And sometimes `reference_wrapper` really is what you want to use and OPs question seemed like one such potential situation to me. – Jesper Juhl May 17 '19 at 18:17
  • 2
    @JesperJuhl At least I didn't find a way how to realize that with a `std::reference_wrapper`. – πάντα ῥεῖ May 17 '19 at 18:18