1

recently I was thinking about how it would be nice if iterators implicitly converted to bool so you could do

auto it = find(begin(x),end(x), 42);
if (it)  //not it!=x.end();
{
}

but thinking about it I realized that this would mean that either it would had to be set to "NULL", so that you couldnt use the it directly if you want to do something with it (you would have to use x.end()) or you could use it but iter size would have to be bigger(to store if what it points to was .end() or not). So my questions are:

  1. Is the syntax in my example achievable without breaking current code and without increasing the sizeof iterator?
  2. would implicitly conversion to bool cause some problems?
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • 4
    Achievable: Probably. Desirable No. Implicit conversion is not something to take lightly unless designed correctly can cause all sorts of problems. – Martin York Oct 05 '12 at 16:01
  • 2
    Only in some cases would that work, and so it wouldn't be able to be used generically. The iterator would have to know about its container. In the case of pointers, this is not possible, and by extension, since vector(and std::array and basic_string) iterators may be simply pointers, it's not possible there either. Doing it for list iterators would make splicing terrible, so not feasible there either. And of course, some iterators aren't even part of containers. – Benjamin Lindley Oct 05 '12 at 16:05
  • I may be wrong here, but I'm of the opinion that iterators should be seen as isomorphic to pointers. Incrementing a pointer will not result in a value that can be implicitly cast to `false` (until you wrap around at the end of your pointer size). You should therefore instead be checking to see if `*it` evaluates to false. This may also be a terrible idea, but at least it is _equivalent_ to pointer behavior (eg. for null-terminated buffers) – Rook Oct 05 '12 at 16:06
  • 1
    @Rook Why should iterators be isomorphic to pointers? Pointers (including smart pointers) are isomorphic to pointers. And iterator should iterate, and that includes knowing when to stop. – James Kanze Oct 05 '12 at 17:05
  • 1
    @NoSenseEtAl: Actually, unlike the bashing you got, you are right in a way. Iterators are **too low-level an abstraction** for general use. A much easier (and yet similarly generic) idea is that of ranges/views, which naturally know both ends. Not only does it allow checks like you propose, but by unifying the begin/end in a single structure, in also avoids both the tedium of systematically passing one more argument to the STL functions and... the risks that come with it. – Matthieu M. Oct 05 '12 at 19:21
  • @Loki: Actually, you don't need an *implicit* conversion for the `if` test, [an `explicit` conversion operator will do](http://stackoverflow.com/questions/6242768/is-the-safe-bool-idiom-obsolete-in-c11). – Xeo Oct 06 '12 at 13:16

4 Answers4

3

You are working on the assumption that iterators are a way of accessing a container. They allow you to do that, but they also allow many more things that would clearly not fit your intended operations:

auto it = std::find(std::begin(x), std::next(std::begin(x),10), 42 );
    // Is 42 among the first 10 elements of 'x'?

auto it = std::find(std::istream_iterator<int>(std::cout),
                    std::istream_iterator<int>(), 42 );
    // Is 42 one of the numbers from standard input?

In the first case the iterator does refer to a container, but the range where you are finding does not enclose the whole container, so it cannot be tested against end(x). In the second case there is no container at all.

Note that an efficient implementation of an iterator for many containers holds just a pointer, so any other state would increase the size of the iterator.

Regarding conversions to any-type or bool, they do cause many problems, but they can be circumvented in C++11 by means of explicit conversions, or in C++03 by using the safe-bool idiom.

You are probably more interested on a different concept: ranges. There are multiple approaches to ranges, so it is not so clear what the precise semantics should be. The first two that come to mind are Boost.Iterator and an article by Alexandrescu I read recently called On Iteration.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
2

Two reasons this wouldn't work:

First, it's possible to use raw pointers as iterators (usually into an array):

int data[] = { 50, 42, 37, 5 };
auto it = find(begin(data), end(data), 42);

Second, you don't have to pass the actual end of the container to find; to e.g. find the first space character before a period:

auto sentence = "Hello, world.";
auto it1 = find(begin(sentence), end(sentence), '.');
auto it2 = find(begin(sentence), it1, ' ');
ecatmur
  • 152,476
  • 27
  • 293
  • 366
2

There's very little that can be done with a single iterator. A pair of iterators defines a sequence consisting of the elements; the first iterator points to the first element and the second iterator points one past the end of the last element. There's no way, in general, for the first iterator to know when it's been incremented to match the second iterator. Algorithms do that, because they have both iterators and can tell when the work is done. For example:

std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);

// copy the contents of the vector:
std::copy(somewhere, vec.begin(), vec.end());
// copy the first two elements of the vector:
std::copy(somewhere, vec.begin(), vec.begin() + 2);

In both calls to copy, vec.begin() is the same iterator; the algorithm does different things because it got the second iterator that tells it when to stop.

Granted, it's possible to design a different kind of iterator that contains both the beginning and the end of the sequence (as Java does), but that's not how C++ iterators are designed. There's discussion of standardizing the notion of a "range" that holds two iterators (the new range-based for loop is a first step toward that).

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
2

Well, you probably don't want an implicit conversion, but requiring two separate objects to determine when iteration is done is clearly a design error. It's not so much because of the if or the for (although using a single iterator would make these clearer as well); it's really because it makes functional decomposition and filtering iterators extreamly difficult, if not impossible.

Fundamentally, STL iterators are closer to smart pointers than they are to iterators. There are times when such pointers are appropriate, but they aren't a good replacement for iterators.

James Kanze
  • 150,581
  • 18
  • 184
  • 329