7
\$\begingroup\$

I needed to pass a const reference to part of a std::vector without copy. I posted the previous version in my earlier question, thank you for the great support! As far as I know this version supports not only std::vectors but any kind of iterator based continuous containers!

Some research, particularly this SO question, suggested that it is not something supported by default(at least in pre-c++20 std), so I created my own code for it:

#include <iterator>
#include <vector>

template <typename Iterator = std::vector<double>::const_iterator>
class ConstVectorSubrange{
public:
  using T = typename Iterator::value_type;

  ConstVectorSubrange(Iterator start_, std::size_t size_)
  : start(start_)
  , range_size(size_)
  { }

  ConstVectorSubrange(Iterator begin_, Iterator end_)
  : start(begin_)
  , range_size(std::distance(start, end_))
  { }

  const T& operator[](std::size_t index) const{
    assert(index < range_size);
    return *std::next(start, index);
  }
  const T& front() const{
    return *begin();
  }
  const T& back() const{
    return *std::next(end(), -1);
  }
  std::size_t size() const{
    return range_size;
  }
  Iterator begin() const{
    return start;
  }
  Iterator end() const{
    return std::next(start, range_size);
  }

private:
  const Iterator start;
  const std::size_t range_size;
};

In C++20 the functionality is available in std::span, so I am trying to achieve this with c++17 latest. Target for my code is c++11, but c++17 features are also present in the project I am using this in. I'm not sure I would go up to c++20 as I'm still not sure how widespread it is yet, but I'm still open to it.

An example of using this would be a member function where not the whole array is visible, although copies are to be avoided:

#include <vector>

class BigData{
  std::vector<int> big_array = vector<int>(10000);
public:

  ConstVectorSubrange get_last_five_thousand_elements(){
    return{(big_array.end()-5000), big_array.end()};
  }

};

How can this code be improved? I tried to remove the dependent typename, but I could only do that with C++20 std::iter_reference_t. Is there no other way to deduce T explicitly?

\$\endgroup\$
1
  • \$\begingroup\$ Thanks, I corrected it \$\endgroup\$ Nov 27 '21 at 13:38
5
\$\begingroup\$

With C++17 the compiler should be able to deduce the template type automatically by using the following:

template <typename Iterator>
class ConstVectorSubrange {
public:
    using T = typename Iterator::value_type;
    // ...
}

int main() 
{
    std::vector v{ 1, 2, 3, 4, 5, 6, 7, 8 };
    ConstVectorSubrange range(v.begin() + 2, v.end() - 2);
}

I'm not sure why you have included <iterator> and failed to include <cassert>?

Good:

I like that you can now use the foreach loop:

std::vector v{ 7, 7, 3, 4, 5, 6, 7, 7, 8, 9 };
ConstVectorSubrange vsr(v.begin() + 4, v.end());
for (int i : vsr)
    std::cout << i << "\n";

And all the cool stuff from <algorithm>

std::cout << std::count(vsr.begin(), vsr.end(), 7) << "\n";

Ideas:

It would be pretty cool if you could include a step or stride to have a slice of the vector. Something like this:

std::vector v{ 1, 2, 3, 4, 5, 6 };
std::size_t step = 2;
ConstVectorSubrange r(v.begin() + 1, v.end(), step);
for (int i : r)
    std::cout << i << "\n";
// expected results 2, 4, 6
\$\endgroup\$
4
  • 1
    \$\begingroup\$ <assert.h> is not a C++ header. You probably mean <cassert>. \$\endgroup\$
    – indi
    Nov 27 '21 at 19:26
  • \$\begingroup\$ @indi thanks, updated. \$\endgroup\$
    – jdt
    Nov 27 '21 at 20:11
  • \$\begingroup\$ Thank you very much! :) \$\endgroup\$ Nov 28 '21 at 13:56
  • 1
    \$\begingroup\$ @DavidTóth, cool! You got so close that I almost feel like I should have added a spoiler alert ;-) \$\endgroup\$
    – jdt
    Nov 28 '21 at 14:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.