0

C++ rookie here. I wrote a function that returns an iterator from a vector, and I want to use this iterator to iterate through the vector from beginning to end. However, the vector iterator is used like this

for (auto iterator = vec.begin(); iterator != vec.end(); iterator ++) {
// do something here
}

which means I also need a vec.end() for this to work. Is there anyway I can only use vec.begin() to iterate through a vector, as I commonly did in python

for value in some_iterator:
# do something

edit: some irrelevant update:

I see some comments about my python iterator vs iterable. The iterator can indeed be used this way (at least in Python 3). For example:

some_list = [1,2,3,4]
some_iterator = iter(some_list)
for value in some_iterator:
    print(value)
DiveIntoML
  • 2,347
  • 2
  • 20
  • 36
  • 2
    Do you want to iterate through the entire vector or up to / from a specific point? What is the iterator that the function returns? – Fibbs Jul 30 '18 at 15:42
  • Just a note, an iterator is not the same as the values in the vector, as is your python example. – Ben Jones Jul 30 '18 at 15:46

4 Answers4

7

C++ is not Python. A pair of std::vector::iterators is required to denote a range (the start and one past the end). If you only have one Iterator, there's nothing you can safely do with it (besides compare it with itself, which is vacuously true).

You are confusing the python concept of a Python iterable, which is a sequence of values. This is distinct from an Python iterator, which is an object with a next() (__next__() in Python 3) that returns the next value or throws StopIteration

for value in some_iterable:
    #do something here

Which corresponds to roughly

_iter = some_iterable.__iter__()
while True:
    try
        value = _iter.__next__()
    except StopIteration:
        break;
    #do something here

You probably want a ranged-for statement

for (auto val : vec){
    // do something here
}

Which corresponds to roughly

{
    auto && __range = vec; 
    for (auto __begin = begin(__range), __end = end(__range);  __begin != __end; ++__begin) { 
        auto val = *__begin; 
        // do something here
    } 
} 

(In both versions, the local variables with leading _ don't actually exist, the names are expository)

A C++ iterator splits out the returning of values (*it and it->) from moving (++it etc); and from stopping (it != end), where end points one past the final element. A Python iterator does it all in next().

Caleth
  • 52,200
  • 2
  • 44
  • 75
  • _"A `std::vector::iterator` only denotes half of a range"_ I'm not sure what this means exactly. An iterator can really point to anything inside the vector. For example: `auto it = vec.begin() + 3;`. Assuming `vec` has at least 4 elements, `it` now points to the 4th element. – Ben Jones Jul 30 '18 at 16:10
  • @BenJones I've rephrased that section – Caleth Jul 30 '18 at 16:19
  • 2
    @BenJones That is still half of a range, namely the beginning of that range (or the end). You don't know where the range ends if you only have the beginning and vice-versa. Surely the iterator could point to the fourth element, but if you start iterating, where do you end? Four elements later? 12? It's unknown as long as you don't have the other end of the range – Athos vk Jul 30 '18 at 16:19
  • I guess I was confused as the original wording seemed to imply an iterator could only point to the beginning or end of the _vector_. But the new wording/explanation makes a lot more sense. – Ben Jones Jul 30 '18 at 17:44
2

As others have already pointed out, saying:

for value in some_iterator:
# do something

is not correct: you are iterating in an array, or more generally in an "iterable", by using it's internal __iter__ iterators. So it's more correct:

for value in some_iterable:
# do something

Iterables in python are "containers" in C++. std::vector is a container.

I think you have two options:

  • if you want to have the full container ("from beginning to end", so I guess this is the case), just return it. You can return it by reference, if its life continues after the function exiting: in fact in python arrays are copied by reference, so this is equivalent. However, if it's a temporary vector, you can return it by value, but don't worry of it being copied: with modern C++ and std::move operator this is not going to happen.

The "old way" was to pass an empty vector by reference to the function, and let the function fill the vector.

However I usually don't like to use std::pair for this kind of operations, but to forge my temporary type for the specific aim.

For example, this could be the signature of your function:

template<typename TYPE>
struct GetRangeResult {
  std::vector<TYPE>::const_iterator begin;
  std::vector<TYPE>::const_iterator end;
};

template<typename TYPE>
GetRangeResult<TYPE> GetRange(/* your args */) {
  GetRangeResult<TYPE> result;
  // the method here fills result.begin and result.end
  return result;
}

and you would use it:

auto range = GetRange(/* your args */);
for (auto it=range.begin; it!=range.end; ++it) {
  ...
}

Again you cannot do it if the vector is temporary within the GetRange() function.

Sigi
  • 4,826
  • 1
  • 19
  • 23
  • 1
    Agreed, have your function return a pair of iterators - problem solved (except when the vector is temporary, as you have said). You would have to use `GetRangeResult result;` and to deduce the _someType_ to use the code you propose, though. – Gem Taylor Jul 30 '18 at 19:47
1

I wrote a function that returns an iterator from a vector, and I want to use this iterator to iterate through the vector from beginning to end.

You need 2 iterators in order to iterate through a container - the starting iterator, and the ending iterator. For instance, change your function to return a std::pair of iterators instead of a single iterator, then you can iterate, eg:

template<typename Container>
std::pair<Container::iterator, Container::iterator> func(Container &c) {
    return std::make_pair(c.begin(), c.end());
}

auto p = func(vec);
for (auto iter = p.first; iter != p.second; ++iter) {
    // do something here
}

Is there anyway I can only use vec.begin() to iterate through a vector

No. Without a second iterator, you wouldn't know when to stop iterating.

as I commonly did in python

The closest thing to that is a range-based for loop, but that requires having access to the vector itself, it using iterators manually (the loop uses iterators internally for you):

for (auto &elem : vec) {
    // do something here
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

There is no way to get the a reference to the original container from an iterator. That means if you have a function that, say, returns an iterator to the mid point of a vector it is effectively useless unless you know which vector it came from.

Your options are to either:

  1. Only use the function when you have access to the vector you are operating on (usage akin to something like std::find).
  2. Return from the function a set of iterators specifying the range you want to iterate over.
  3. Return from the function a reference to the original vector alongside an iterator denoting the offset you want to iterate to/from.

Without knowing the purpose of the function it's hard to give more specific advice. I suspect there is probably a better that of structuring your code.

Fibbs
  • 1,350
  • 1
  • 13
  • 23