How to use an iterator?

43,798

Solution 1

That your code compiles at all is probably because you have a using namespace std somewhere. (Otherwise vector would have to be std::vector.) That's something I would advise against and you have just provided a good case why:
By accident, your call picks up std::distance(), which takes two iterators and calculates the distance between them. Remove the using directive and prefix all standard library types with std:: and the compiler will tell you that you tried to pass a vector <point>::iterator where a point* was required.

To get a pointer to an object an iterator points to, you'd have to dereference the iterator - which gives a reference to the object - and take the address of the result: &*ii.
(Note that a pointer would perfectly fulfill all requirements for a std::vector iterator and some earlier implementations of the standard library indeed used pointers for that, which allowed you to treat std::vector iterators as pointers. But modern implementations use a special iterator class for that. I suppose the reason is that using a class allows overloading functions for pointers and iterators. Also, using pointers as std::vector iterators encourages mixing pointers and iterators, which will prevent the code to compile when you change your container.)

But rather than doing this, I suggest you change your function so that it takes references instead (see this answer for why that's a good idea anyway.) :

float distance(const point& p1, const point& p2)
{
    return sqrt((p1.x - p2.x)*(p1.x - p2.x) +
                (p1.y - p2.y)*(p1.y - p2.y));
}

Note that the points are taken by const references. This indicates to the caller that the function won't change the points it is passed.

Then you can call it like this: distance(*ii,*jj).


On a side note, this

typedef struct point {
    float x;
    float y;
} point;

is a C-ism unnecessary in C++. Just spell it

struct point {
    float x;
    float y;
};

That would make problems if this struct definition ever was to parse from a C compiler (the code would have to refer to struct point then, not simply point), but I guess std::vector and the like would be far more of a challenge to a C compiler anyway.

Solution 2

By coincidence, you're actually using a built-in STL function "distance", which calculates the distance between iterators, instead of calling your own distance function. You need to "dereference" your iterators to get the contained object.

cout << distance(&(*ii), &(*jj)) << " ";

As you can see from the syntax above, an "iterator" is quite a lot like a generalized "pointer". The iterator cannot be used as "your" object type directly. In fact iterators are so similar to pointers that many standard algorithms that operate on iterators work fine on pointers as well.

As Sbi noted: your distance function takes pointers. It would be better rewritten as taking const references instead, which would make the function more "canonical" c++, and make the iterator dereference syntax less painful.

float distance(const point& i_p1, const point& i_p2)
{
    return sqrt((p1.x - p2.x)*(p1.x - p2.x) +
                (p1.y - p2.y)*(p1.y - p2.y));
}

cout << distance(*ii, *jj) << " ";

Solution 3

You might do a couple of things:

  1. Make the distance() function take references to point objects. This is really just to make things more readable when calling the distance() function:
    float distance(const point& p1, const point& p2)
    {
        return sqrt((p1.x - p2.x)*(p1.x - p2.x) +
                    (p1.y - p2.y)*(p1.y - p2.y));
    }
    
  2. Dereference your iterators when calling distance()so you're passing the point objects:
    distance( *ii, *jj)
    
    If you don't change the interface of the distance() function, you might have to call it using something like the following to get appropriate pointers:
    distance( &*ii, &*jj)
    
Share:
43,798

Related videos on Youtube

Peter Mortensen
Author by

Peter Mortensen

Experienced application developer. Software Engineer. M.Sc.E.E. C++ (10 years), software engineering, .NET/C#/VB.NET (12 years), usability testing, Perl, scientific computing, Python, Windows/Macintosh/Linux, Z80 assembly, CAN bus/CANopen. Contact I can be contacted through this reCAPTCHA (requires JavaScript to be allowed from google.com and possibly other(s)). Make sure to make the subject specific (I said: specific. Repeat: specific subject required). I can not stress this enough - 90% of you can not compose a specific subject, but instead use some generic subject. Use a specific subject, damn it! You still don't get it. It can't be that difficult to provide a specific subject to an email instead of a generic one. For example, including meta content like "quick question" is unhelpful. Concentrate on the actual subject. Did I say specific? I think I did. Let me repeat it just in case: use a specific subject in your email (otherwise it will no be opened at all). Selected questions, etc.: End-of-line identifier in VB.NET? How can I determine if a .NET assembly was built for x86 or x64? C++ reference - sample memmove The difference between + and &amp; for joining strings in VB.NET Some of my other accounts: Careers. [/]. Super User (SU). [/]. Other My 15 minutes of fame on Super User My 15 minutes of fame in Denmark Blog. Sample: Jump the shark. LinkedIn @PeterMortensen (Twitter) Quora GitHub Full jump page (Last updated 2021-11-25)

Updated on July 05, 2022

Comments

  • Peter Mortensen
    Peter Mortensen almost 2 years

    I'm trying to calculate the distance between two points. The two points I stored in a vector in C++: (0,0) and (1,1).

    I'm supposed to get results as

    0
    1.4
    1.4
    0
    

    But the actual result that I got is

    0
    1
    -1
    0
    

    I think there's something wrong with the way I use iterator in vector. How can I fix this problem?

    I posted the code below.

    typedef struct point {
        float x;
        float y;
    } point;
    
    float distance(point *p1, point *p2)
    {
        return sqrt((p1->x - p2->x)*(p1->x - p2->x) +
                    (p1->y - p2->y)*(p1->y - p2->y));
    }
    
    int main()
    {
        vector <point> po;
        point p1; p1.x = 0; p1.y = 0;
        point p2; p2.x = 1; p2.y = 1;
        po.push_back(p1);
        po.push_back(p2);
    
        vector <point>::iterator ii;
        vector <point>::iterator jj;
        for (ii = po.begin(); ii != po.end(); ii++)
        {
            for (jj = po.begin(); jj != po.end(); jj++)
            {
                cout << distance(ii,jj) << " ";
            }
        }
        return 0;
    }
    
  • Puppy
    Puppy over 9 years
    This answer is incorrect. std::distance can be picked up by ADL on a std:: iterator, so it may form part of the candidate set regardless of whether or not std is used.
  • sbi
    sbi over 9 years
    @Puppy: That is indeed true (and for 2.5 years nobody noticed), but this isn't all my answer said. Passing points per const point& p1 will solve this problem, too.
  • Ben Voigt
    Ben Voigt about 9 years
    @sbi: No, it won't solve the problem. It will still be possible to mistakenly write distance(ii, jj) and get std::distance.
  • Peregring-lk
    Peregring-lk about 8 years
    Though nobody sais that, the most obvious way to force your version of distance is called, is writting ::distance(...) instead of distance(...) when you call the function. Your distance function is defined in the global namespace, so, you can qualify the name of your function with an empty prefix ::distance (and of course, you must dereference the iterator to call it properly).
  • amanuel2
    amanuel2 about 8 years
    using namespace std; is an extremely bad practice.. Thats what to be thought here.. Spread the word!
  • David Hammen
    David Hammen over 4 years
    I upvoted this ages ago, but had to change it to a downvote. The code in question is even more pernicious than it first appears. "That your code compiles at all is probably because you have a using namespace std somewhere" is not correct. All it takes is an explicit #include <utility>, or even worse, an implicit #include <utility>. The implicit #include comes "for free!!" with #include <vector> or #include <iostream> on many compilers, including gcc, clang, and llvm. (continued)
  • David Hammen
    David Hammen over 4 years
    If std::distance is visible, the call to the distance function in namespace std is the preferred match, even without the std:: prefix, thanks to Argument Dependent Lookup. Both arguments to the distance call in the OP's code are of a type defined in namespace std. ADL thus very nicely says to look for functions in namespace std named distance in addition to functions named distance in the current or global namespaces, even though the function call is not prefixed with std:: and even if the code does not used the awful using namespace std construct.
  • David Hammen
    David Hammen over 4 years
    The problem is that function template std::distance provides an exact match while the non-template ::distance function does not match the arguments at all. It's pretty obvious which function is the better match.