STL non-copying wrapper around an existing array? - c++

Is it possible to create an STL-like container, or even just an STL-style iterator, for an existing array of POD-type elements?
For example, suppose I have an array of ints. It would be convenient to be able to call some of the STL functions, such as find_if, count_if, or sort directly on this array.
Non-solution: copying the entire array, or even just references to the elements. The goal is to be very memory- and time-saving while hopefully allowing use of other STL algorithms.

You can call many of the STL algorithms directly on a regular C style array - they were designed for this to work. e.g.,:
int ary[100];
// init ...
std::sort(ary, ary+100); // sorts the array
std::find(ary, ary+100, pred); find some element
I think you'll find that most stuff works just as you would expect.

You can use an inline function template so that you don't have to duplicate the array index
template <typename T, int I>
inline T * array_begin (T (&t)[I])
{
return t;
}
template <typename T, int I>
inline T * array_end (T (&t)[I])
{
return t + I;
}
void foo ()
{
int array[100];
std::find (array_begin (array)
, array_end (array)
, 10);
}

All the STL algorithms use iterators.
A pointer is a valid iterator into an array of objects.
N.B.The end iterator must be one element past the end of the array. Hence the data+5 in the following code.
#include <algorithm>
#include <iostream>
#include <iterator>
int main()
{
int data[] = {4,3,7,5,8};
std::sort(data,data+5);
std::copy(data,data+5,std::ostream_iterator<int>(std::cout,"\t"));
}

You can use Boost.Array to create a C++ array type with STL semantics.
using arrays:
int a[100];
for (int i = 0; i < 100; ++i)
a[i] = 0;
using boost.arrays:
boost::array<int,100> a;
for (boost::array<int,100>::iterator i = a.begin(); i != a.end(); ++i)
*i = 0;
Update: With C++11, you can now use std::array.

A pointer is a valid model of an iterator:
struct Bob
{ int val; };
bool operator<(const Bob& lhs, const Bob& rhs)
{ return lhs.val < rhs.val; }
// let's do a reverse sort
bool pred(const Bob& lhs, const Bob& rhs)
{ return lhs.val > rhs.val; }
bool isBobNumberTwo(const Bob& bob) { return bob.val == 2; }
int main()
{
Bob bobs[4]; // ok, so we have 4 bobs!
const size_t size = sizeof(bobs)/sizeof(Bob);
bobs[0].val = 1; bobs[1].val = 4; bobs[2].val = 2; bobs[3].val = 3;
// sort using std::less<Bob> wich uses operator <
std::sort(bobs, bobs + size);
std::cout << bobs[0].val << std::endl;
std::cout << bobs[1].val << std::endl;
std::cout << bobs[2].val << std::endl;
std::cout << bobs[3].val << std::endl;
// sort using pred
std::sort(bobs, bobs + size, pred);
std::cout << bobs[0].val << std::endl;
std::cout << bobs[1].val << std::endl;
std::cout << bobs[2].val << std::endl;
std::cout << bobs[3].val << std::endl;
//Let's find Bob number 2
Bob* bob = std::find_if(bobs, bobs + size, isBobNumberTwo);
if (bob->val == 2)
std::cout << "Ok, found the right one!\n";
else
std::cout << "Whoops!\n";
return 0;
}

Related

How can I convert std::vector<T> to a vector of pairs std::vector<std::pair<T,T>> using an STL algorithm?

I have a vector of integers:
std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
Given that values.size() will always be even.
I simply want to convert the adjacent elements into a pair, like this:
std::vector<std::pair<int,int>> values = { {1,2}, {3,4} , {5,6}, {7,8} ,{9,10} };
I.e., the two adjacent elements are joined into a pair.
What STL algorithm can I use to easily achieve this? Is it possible to achieve this through some standard algorithms?
Of course, I can easily write an old school indexed for loop to achieve that. But I want to know what the simplest solution could look like using rangebased for loops or any other STL algorithm, like std::transform, etc.
Once we have C++23's extension to <ranges>, you can get most of the way there with std::ranges::views::chunk, although that produces subranges, not pairs.
#include <iostream>
#include <ranges>
#include <vector>
int main()
{
std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
auto chunk_to_pair = [](auto chunk)
{
return std::pair(*chunk.begin(), *std::next(chunk.begin()));
};
for (auto [first, second] : values | std::ranges::views::chunk(2) | std::ranges::views::transform(chunk_to_pair))
{
std::cout << first << second << std::endl;
}
}
Alternatively, you could achieve a similar result by ziping a pair of strided views
#include <iostream>
#include <ranges>
#include <vector>
int main()
{
std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
auto odds = values | std::ranges::views::drop(0) | std::ranges::views::stride(2);
auto evens = values | std::ranges::views::drop(1) | std::ranges::views::stride(2);
for (auto [first, second] : std::ranges::views::zip(odds, evens))
{
std::cout << first << second << std::endl;
}
}
That last one can be generalised to n-tuples
template <size_t N>
struct tuple_chunk_t
{
template <typename R, size_t... Is>
auto impl(R && r, std::index_sequence<Is...>)
{
using namespace ranges::view;
return zip(r | drop(Is) | stride(N)...);
}
template <typename R>
auto operator()(R && r) const
{
return impl(std::forward<R>(r), std::make_index_sequence<N>{});
}
template <typename R>
friend auto operator|(R && r, chunk_t)
{
return impl(std::forward<R>(r), std::make_index_sequence<N>{});
}
};
template <size_t N>
constexpr tuple_chunk_t<N> tuple_chunk;
I'm not sure why you would require a standard algorithm when writing it yourself is roughly 5 lines of code (plus boilerplate):
template<class T>
std::vector<std::pair<T, T>> group_pairs(const std::vector<T>& values)
{
assert(values.size() % 2 == 0);
auto output = std::vector<std::pair<T, T>>();
output.reserve(values.size()/2);
for(size_t i = 0; i < values.size(); i+=2)
output.emplace_back(values[i], values[i+1]);
return output;
}
And call it like so:
std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
auto result = group_pairs(values)
Live Demo
I am not aware of a standard algorithm that does what you want directly (though I am not very familiar with C++20 and beyond). You can always write a loop and most loops can be expressed via std::for_each which is a standard algorithm.
As you are accumulating elements in pairs, I would give std::accumulate a try:
#include <vector>
#include <numeric>
#include <iostream>
struct pair_accumulator {
std::vector<std::pair<int,int>> result;
int temp = 0;
bool set = false;
pair_accumulator& operator+(int x){
if (set) {
result.push_back({temp,x});
set = false;
} else {
temp = x;
set = true;
}
return *this;
}
};
int main() {
std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
auto x = std::accumulate(values.begin(),values.end(),pair_accumulator{}).result;
for (const auto& e : x) {
std::cout << e.first << " " << e.second << "\n";
}
}
Whether this is simpler than writing a plain loop is questionable admittedly.
If possible I would try to not transform the vector. Instead of accessing result[i].first you can as well use values[i*2] and similar for second. If this is not feasible the next option is to populate a std::vector<std::pair<int,int>> from the start so you don't have to do the transformation. For the first, depending on what you need in details, the following might be a start:
#include <vector>
#include <iostream>
struct view_as_pairs {
std::vector<int>& values;
struct proxy {
std::vector<int>::iterator it;
int& first() { return *it;}
int& second() { return *(it +1); }
};
proxy operator[](size_t index){
return proxy{values.begin() + index*2};
}
size_t size() { return values.size() / 2;}
};
int main() {
std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
view_as_pairs v{values};
for (size_t i=0; i < v.size(); ++i){
std::cout << v[i].first() << " " << v[i].second() << "\n";
}
}
TL;DR: Consider if you can avoid the transformation. If you cannot avoid it, it is probably cleanest to write a loop. Standard algorithms help often but not always.
OK, I hinted in the comments about using std::adjacent_find, so here is how you would do this.
And yes, many (even myself) considers this a hack, where we are using a tool meant for something else to make short work of solving a seemingly unrelated problem:
#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>
int main()
{
//Test data
std::vector<int> v = {1,2,3,4,5,6,7,8,9,10};
// results
std::vector<std::pair<int,int>> result;
// save flag
bool save_it = true;
// Use std::adjacent_find
std::adjacent_find(v.begin(), v.end(), [&](int n1, int n2)
{ if (save_it) result.push_back({n1,n2}); save_it = !save_it; return false; });
for (auto& pr : result)
std::cout << pr.first << " " << pr.second << "\n";
}
Output:
1 2
3 4
5 6
7 8
9 10
The way it works is we ignore the second, fourth, sixth, etc. pairs, and only save the first, third, fifth, etc. pairs. That's controlled by a boolean flag variable, save_it.
Note that since we want to process all pairs, the std::adjacent_find predicate always returns false. That's the hackish part of this solution.
The solutions so far try to use the std::vector iterators as input to the algorithms directly. How about defining a custom iterator that returns a std::pair and has strides of 2? Creating the vector of pairs is then a one-liner that uses std::copy. The iterator effectively provides a "view" onto the original vector in terms of pairs. This also allows the use of many of the standard algorithms. The following example could also be generalized quite a bit to work with most container iterators, i.e. you do the difficult work of defining such an iterator once and then you can apply it to all sorts of containers and algorithms. Live example: https://godbolt.org/z/ceEsvKhzd
#include <vector>
#include <algorithm>
#include <iostream>
#include <cassert>
struct pair_iterator {
using difference_type = std::vector<int>::const_iterator::difference_type;
using value_type = std::pair<int, int>;
using pointer = value_type*;
using reference = value_type; // Not a pair&, but that is ok for LegacyIterator
// Can't be forward_iterator_tag because "reference" is not a pair&
using iterator_category = std::input_iterator_tag;
reference operator*()const { return {*base_iter, *(base_iter + 1)}; }
pair_iterator & operator++() { base_iter += 2; return *this; }
pair_iterator operator++(int) { auto ret = *this; ++(*this); return ret; }
friend bool operator==(pair_iterator lhs, pair_iterator rhs){
return lhs.base_iter == rhs.base_iter;
}
friend bool operator!=(pair_iterator lhs, pair_iterator rhs){
return lhs.base_iter != rhs.base_iter;
}
std::vector<int>::const_iterator base_iter{};
};
auto pair_begin(std::vector<int> const & v){ assert(v.size()%2==0); return pair_iterator{v.begin()}; }
auto pair_end(std::vector<int> const & v){ assert(v.size()%2==0); return pair_iterator{v.end()}; }
int main()
{
std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
std::vector<std::pair<int, int>> pair_values;
std::copy(pair_begin(values), pair_end(values), std::back_inserter(pair_values));
for (auto const & pair : pair_values) {
std::cout << "{" << pair.first << "," << pair.second << "} ";
}
std::cout << std::endl;
}

C++ - Iterate over vector of doubles as tuples

I have a C++ vector of doubles, which is guaranteed to have an even number of elements. This vector stores the coordinates of a set of points as x, y coordinates:
A[2 * i ] is the x coordinate of the i'th point.
A[2 * i + 1] is the y coordinate of the i'th point.
How to implement an iterator that allows me to use STL style algorithms (one that takes an iterator range, where dereferencing an iterator gives back the pair of doubles corresponding to the x, y coordinates of the corresponding point) ?
I'm using C++17 if that helps.
We can use range-v3, with two adapters:
chunk(n) takes a range and adapts it into a range of non-overlapping ranges of size n
transform(f) takes a range of x and adapts it into a range of f(x)
Putting those together we can create an adaptor which takes a range and yields a range of non-overlapping pairs:
auto into_pairs = rv::chunk(2)
| rv::transform([](auto&& r){ return std::pair(r[0], r[1]); });
Using the r[0] syntax assumes that the input range is random-access, which in this case is fine since we know we want to use it on a vector, but it can also be generalized to work for forward-only ranges at the cost of a bit more syntax:
| rv::transform([](auto&& r){
auto it = ranges::begin(r);
auto next = ranges::next(it);
return std::pair(*it, *next);
})
Demo, using fmt for convenient printing:
int main() {
std::vector<int> v = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5};
auto into_pairs = rv::chunk(2)
| rv::transform([](auto&& r){ return std::pair(r[0], r[1]); });
// prints {(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)}
fmt::print("{}\n", v | into_pairs);
}
It's unclear from the question if you wanted pair<T, T>s or pair<T&, T&>s. The latter is doable by providing explicit types to std::pair rather than relying on class template argument deduction.
C++ is a bit of a moving target - also when it comes to iterators (c++20 has concepts for that...). But would it not be nice to have a lazy solution to the problem. I.e. The tuples get generated on the fly, without casting (see other answers) and without having to write a loop to transform a vector<double> to a vector<tuple<double,double>>?
Now I feel I need a disclaimer because I am not sure this is entirely correct (language lawyers will hopefully point out, if I missed something). But it compiles and produces the expected output. That is something, yes?! Yes.
The idea is to build a pseudo container (which actually is just a facade to an underlying container) with an iterator of its own, producing the desired output type on the fly.
#include <vector>
#include <tuple>
#include <iostream>
#include <iterator>
template <class SourceIter>
struct PairWise {
PairWise() = delete;
PairWise(SourceIter first, SourceIter last)
: first{first}
, last{last}
{
}
using value_type =
typename std::tuple<
typename SourceIter::value_type,
typename SourceIter::value_type
>;
using source_iter = SourceIter;
struct IterState {
PairWise::source_iter first;
PairWise::source_iter last;
PairWise::source_iter current;
IterState(PairWise::source_iter first, PairWise::source_iter last)
: first{first}
, last{last}
, current{first}
{
}
friend bool operator==(const IterState& a, const IterState& b) {
// std::cout << "operator==(a,b)" << std::endl;
return (a.first == b.first)
&& (a.last == b.last)
&& (a.current == b.current);
}
IterState& operator++() {
// std::cout << "operator++()" << std::endl;
if (std::distance(current,last) >= 2) {
current++;
current++;
}
return *this;
}
const PairWise::value_type operator*() const {
// std::cout << "operator*()" << std::endl;
return std::make_tuple(*current, *(current+1));
}
};
using iterator = IterState;
using const_iterator = const IterState;
const_iterator cbegin() const {
return IterState{first,last};
}
const_iterator cend() const {
auto i = IterState{first,last};
i.current = last;
return i;
}
const_iterator begin() const {
// std::cout << "begin()" << std::endl;
return IterState{first,last};
}
const_iterator end() const {
// std::cout << "end()" << std::endl;
auto i = IterState{first,last};
i.current = last;
return i;
}
source_iter first;
source_iter last;
};
std::ostream& operator<<(std::ostream& os, const std::tuple<double,double>& value) {
auto [a,b] = value;
os << "<" << a << "," << b << ">";
return os;
}
template <class Container>
auto pairwise( const Container& container)
-> PairWise<typename Container::const_iterator>
{
return PairWise(container.cbegin(), container.cend());
}
int main( int argc, const char* argv[]) {
using VecF64_t = std::vector<double>;
VecF64_t data{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
for (const auto x : pairwise(data)) {
std::cout << x << std::endl;
}
return 0;
}
Elements in vector are stored in contiguous memory area, so you can use simple pointer arithmetics to access pair of doubles.
operator++ for iterator should skip 2 doubles at every use.
operator* can return tuple of references to double values, so you can read (pair = *it) or edit values (*it = pair).
struct Cont {
std::vector<double>& v;
Cont(std::vector<double>& v) : v(v) {}
struct Iterator : public std::iterator<std::input_iterator_tag , std::pair<double,double>> {
double* ptrData = nullptr;
Iterator(double* data) : ptrData(data) {}
Iterator& operator++() { ptrData += 2; return *this; }
Iterator operator++(int) { Iterator copy(*this); ptrData += 2; return copy; }
auto operator*() { return std::tie(*ptrData,*(ptrData+1)); }
bool operator!=(const Iterator& other) const { return ptrData != other.ptrData; }
};
auto begin() { return Iterator(v.data()); }
auto end() { return Iterator(v.data()+v.size());}
};
int main() {
std::vector<double> v;
v.resize(4);
Cont c(v);
for (auto it = c.begin(); it != c.end(); it++) {
*it = std::tuple<double,double>(20,30);
}
std::cout << v[0] << std::endl; // 20
std::cout << v[1] << std::endl; // 30
}
Demo
There's not an easy "C++" way to do this that's clean and avoids a copy of the original array. There's always this (make a copy):
vector<double> A; // your original list of points
vector<pair<double,double>> points;
for (size_t i = 0; i < A.size()/2; i+= 2)
{
points[i*2] = pair<double,double>(A[i], A[i+1]);
}
The following would likely work, violates a few standards, and the language lawyers will sue me in court for suggesting it. But if we can assume that the sizeof(XY) is the size of two doubles, has no padding, and expected alignment then cheating with a cast will likely work. This assumes you don't need a std::pair
Non standard stuff ahead
vector<double> A; // your original list of points
struct XY {
double x;
double y;
};
static_assert(sizeof(double)*2 == sizeof(XY));
static_assert(alignof(double) == alignof(XY));
XY* points = reinterpret_cast<XY*>(A.data());
size_t numPoints = A.size()/2;
// iterate
for (size_t i = 0; i < numPoints; i++) {
XY& point = points[i];
cout << point.x << "," << point.y << endl;
}
If you can guarantee that std::vector will always have an even number of entries, you could exploit the fact that an vector of doubles will have the same memory layout as a vector of pairs of doubles. This is kind of a dirty trick though so I wouldn't recommend it if you can avoid it. The good news is that the standard guarantees that vector elements will be contiguous.
inline const std::vector<std::pair<double, double>>& make_dbl_pair(std::vector<double>& v)
{
return reinterpret_cast<std::vector<std::pair<double, double>>&>(v);
}
This will only work for iteration with iterators. The size is likely to be double the number of pairs in the vector because it is still a vector of doubles underneath.
Example:
int main(int argc, char* argv[])
{
std::vector<double> dbl_vec = { 0.0, 1.1, 2.2, 3.3, 4.4, 5.5 };
const std::vector<std::pair<double, double>>& pair_vec = make_dbl_pair(dbl_vec);
for (auto it = pair_vec.begin(); it != pair_vec.end(); ++it) {
std::cout << it->first << ", " << it->second << "\n";
}
std::cout << "Size: " << dbl_vec.size() << "\n";
return 0;
}

Using iterators to modify array elements in c++

The problem arise when I am trying to write an insert function that is suppose to move all elements in the array up at the specified location given by the iterator and then insert a new value into the array at the position given by the iterator.
The code is getting errors in the insert function with the following error:
no match for 'operator[]' (operand types are 'std::basic_string [1000]' and 'std::basic_string')
I am new to using iterators, and I think that it is not possible to access array elements with pointers as indices. So I am not sure if there is another way to do this, or do I need to overload the [] operator to make it work some how?
template <class T>
class Vector {
public:
typedef T* iterator;
Vector () { }
T& operator[](unsigned int i) {
return items[i];
}
// T& operator[](iterator i) {
//return items[*i];
//}
iterator begin () {
return &items[0];
}
iterator end () {
return &items[used];
}
int size () { return used; }
iterator insert (iterator position, const T& item) {
for(Vector<T>::iterator i=&items[998]; i>=position; i--)
{
items[*(i+1)]=items[*i];
}
items[*position]= item;
return position;
}
private:
T items[1000];
int used=0;
};
This code is problematic in the sense that it creates 1000 elements of type T, even if logically it is empty. Also, if there are more than 1000 insertions, then the upper elements are discarded.
As for the compilation issues, I have tries to compile the code with Vector<int> and it compiles fine, but crashes. For the same reason it crashes with Vector<int> it does not compile with Vector<std::string>. The Issue is with the type of *i, which is , i.e., std::string in the case of Vector<std::string>. Either use iterator all the way, or use indexes, but don't mix. Using iterators:
for(Vector<T>::iterator i=&items[998]; i>=position; i--)
{
*(i+1)=*i;
}
Edit :
[Just noticed an answer by Scheff that figured this out, after completing this edit]
The above invokes undefined behavior for v.insert(v.begin(), value) since i iterates before items. To avoid that, the iteration should stop before it falls off items:
for(Vector<T>::iterator i=&items[999]; i > position; i--)
{
*i = *(i-1);
}
Also, note that the line following the loop should also be fixed:
items[*position]= item; // <--- BUG: also mixing indexes and iterators
Or using indexes:
for(int i= 998; begin() + i>=position; i--)
{
items[i+1]=items[i];
}
In addition to the answer of Michael Veksler, I tried to get it working (and hence needed a bit longer).
So, with his first proposed fix and additionally
items[*position]= item;
changed to
*position = item;
the following test compiles and runs:
#include <iostream>
int main()
{
Vector<double> vec;
vec.insert(vec.begin(), 1.0);
vec.insert(vec.begin(), 2.0);
vec.insert(vec.begin(), 3.0);
std::cout << "vec.size(): " << vec.size() << '\n';
for (int i = 0; i < vec.size(); ++i) {
std::cout << "vec[" << i << "]: " << vec[i] << '\n';
}
return 0;
}
Output:
vec.size(): 0
Oops!
The update of used is missing in insert() as well:
++used;
And, now it looks better:
vec.size(): 3
vec[0]: 3
vec[1]: 2
vec[2]: 1
The complete MCVE:
#include <iostream>
template <class T>
class Vector {
public:
typedef T* iterator;
Vector () { }
T& operator[](unsigned int i) {
return items[i];
}
// T& operator[](iterator i) {
//return items[*i];
//}
iterator begin () {
return &items[0];
}
iterator end () {
return &items[used];
}
int size () { return used; }
iterator insert (iterator position, const T& item) {
for(Vector<T>::iterator i=&items[998]; i>=position; i--)
{
*(i+1) = *i;
}
*position = item;
++used;
return position;
}
private:
T items[1000];
int used=0;
};
int main()
{
Vector<double> vec;
vec.insert(vec.begin(), 1.0);
vec.insert(vec.begin(), 2.0);
vec.insert(vec.begin(), 3.0);
std::cout << "vec.size(): " << vec.size() << '\n';
for (int i = 0; i < vec.size(); ++i) {
std::cout << "vec[" << i << "]: " << vec[i] << '\n';
}
return 0;
}
Live Demo on coliru
So you can think of an iterator in this context as essentially a glorified pointer to the elements in the array, as you defined in your typedef at the beginning of your class.
When you're trying to access the elements in your array in your insert function, you are essentially dereferencing these pointers to yield the elements themselves and THEN using those elements as indices for your array, hence producing the error that the index is of the wrong type.
So for example suppose you had a Vector<std::string>. Inside the for loop in the insert function, you have this line:
items[*(i+1)]=items[*i];
Because i is an iterator as you defined, i has the type std::string * and hence *i has the type std::string. When you then write items[*i] you are trying to use the std::string as an index for your array which you can't do.
Instead, you should use a line similar to the following:
*(i + 1) = *i
There are also a couple of logical errors in your code, but I'll leave you to find those later on.
Hope this helps!
Have a look at how std::move_backward can be implemented
template< class BidirIt1, class BidirIt2 >
BidirIt2 move_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last)
{
while (first != last) {
*(--d_last) = std::move(*(--last));
}
return d_last;
}
You don't need to move any of the elements past end, and we can rewrite your insert to be similar
iterator insert (iterator position, const T& item) {
for(iterator i = end(), d = end() + 1; i != position; )
{
*(--d) = std::move(*(--i));
}
*position = item;
++used;
return position;
}
Note that this is undefined if you try to insert into a full Vector

sub-vector modifies original vector

I want to extract a sub-vector. Then modify its elements which affects the original vector. My sample code below:
#include <vector>
#include <iostream>
using namespace std;
void printvec(vector<int>& v){
for(int i = 0;i < v.size();i++) {cout << v[i] << " ";}
cout << endl;
}
int main(){
vector<int> original;
for(int i = 1;i <= 10;i++) original.push_back(i);
printvec(original);
vector<int> subvector(original.begin()+4, original.end()-2);
subvector[0]=0;
subvector[1]=0;
printvec(subvector);
printvec(original);
return 0;
}
In above code, subvector does not modify vector. Can some one point me to an elegant way to make a subvector which modifies original vector (hopefully without explicit use of pointers if possible).
If you don't want to use a pointer, you could create a slice class to forward the work to - which will just be a pair of iterators and whatever other operations you might need:
template <typename T>
class slice {
using iterator = typename T::iterator;
using reference = typename std::iterator_traits<iterator>::reference;
slice(iterator first, iterator last)
: first(first), last(last)
{ }
reference operator[](size_t idx)
{
return *std::next(first, idx);
}
iterator begin() const { return first; }
iterator end() const { return last; }
private:
iterator first, last;
};
With that, you can do your slicing thusly:
slice<vector<int>> subvector(original.begin()+4, original.end()-2);
subvector[0]=0; // changes original[4]
subvector[1]=0; // changes original[5]
If you change your printvec to take an arbitrary container and use a range-for to iterate over it, you can print the subvector too. It will contain:
0 0 7 8
The line:
vector<int> subvector(original.begin()+4, original.end()-2);
creates a new vector and copies the elements from original.begin()+4 to original.end()-2 to the new one. Even with pointers, there is no way I would call elegant to achieve what you want, because many change to the original vector (rezise / push_back) could potentially invalidate the pointers to its elements.
Depending on the exact functionality you want to implement, you can use a class like this:
#include <vector>
#include <iostream>
using namespace std;
template<class T>
class Subvector {
std::vector<T>*const vec;
size_t start;
size_t end;
public:
Subvector(std::vector<T>& vector, size_t start, size_t end) :
vec(&vector),
start(start),
end(end)
{}
size_t size() const { return end - start; }
T& operator[](size_t i) {
return (*vec)[start + i];
}
const T& operator[](size_t i) const {
return (*vec)[start + i];
}
};
template<class VEC>
void printvec(const VEC& v){
for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; }
cout << endl;
}
int main(){
vector<int> original;
for (int i = 1; i <= 10; i++) original.push_back(i);
printvec(original);
Subvector<int> subvector(original,4, original.size() - 2);
subvector[0] = 0;
subvector[1] = 0;
printvec(subvector);
printvec(original);
return 0;
}

iterating through multiset of structs

I'm not getting the syntax right. Lets say I have this...
#include <set>
...
struct foo{
int bar;
string test;
};
struct comp{
inline bool operator()(const foo& left,const foo& right){
return left.bar < right.bar;
}
};
int main(){
std::multiset<foo,comp> fooset;
std::multiset<foo,comp>::iterator it;
...//insert into fooset
for (it = fooset.begin(); it != fooset.end(); it++){
//how do i access int bar and string test of each element?
}
return 0;
}
How do i access int bar and string test of each element inside the for loop?
Thanks!
There is a good mnemonic rule that an iterator is a safe C++ abstraction for pointer.
So basically you access the elements through dereferencing syntax:
(*it).bar = 0;
it->test = "";
for (it = fooset.begin(); it != fooset.end(); it++)
{
foo const & f = *it; //const is needed if it is C++11
//use f, e.g
std:: cout << f.bar <<", " << f.test << std::endl;
}
In C++11, you could do this instead:
for(foo const & f : fooset)
{
//use f, e.g
std:: cout << f.bar <<", " << f.test << std::endl;
}