DiveIntoML
DiveIntoML

Reputation: 2527

Iterate std::vector without using vector.end()

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)

Upvotes: 0

Views: 1665

Answers (4)

Sigi
Sigi

Reputation: 4926

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.

Upvotes: 2

Remy Lebeau
Remy Lebeau

Reputation: 596713

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
}

Upvotes: 1

Caleth
Caleth

Reputation: 62894

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().

Upvotes: 7

Fibbs
Fibbs

Reputation: 1420

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.

Upvotes: 0

Related Questions