Topic: Defect Report: member == in container iterators considered harmful


Author: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1999/08/24
Raw View
Valentin Bonnard wrote:

>Doug Harrison wrote:
>
>> P.S. I'm still curious as to how defining these functions as non-members
>> "goes against the spirit of generic programming".
>
>Using non members operators of course doesn't go against
>the spirit of generic programming.
>
>I have never said that.
>
>(And I often use non member operators myself.)

OK, what you actually said in message:

Newsgroups: comp.std.c++
Subject: Re: Defect Report: member == in container iterators considered
harmful
Date: 17 Aug 99 15:50:39 GMT
Message-ID: <37B95B96.7F37@wanadoo.fr>

was the following:

>So mandating non member functions certainly:
>- isn't a solution
>- goes agains the spirit of generic programming

That's what I was asking about.

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1999/08/20
Raw View
Doug Harrison wrote:

>Unless I'm misunderstanding something, making the iterator a nested class of
>the deque template precludes defining operator== as a non-member, because of
>deduction problems, while you can define them as non-members if iterator is
>not a nested class.

Actually, I think I am mistaken. If you have:

template<class T>
struct A
{
   struct B
   {
      friend bool operator==(const B& x,const B& y) { ... }
   };
};

According to 14.5.3/1, this operator== is not a function template, so
template argument deduction doesn't even come into play.

--
Doug Harrison
dHarrison@worldnet.att.net


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/23
Raw View
Darin Adler wrote:
>
> > Darin Adler wrote:
> >> I meant to propose a non-member function, which can be a friend if
> >> necessary. As I understand it, the friend function above *is* a non-member
> >> function.
>
> Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote:
> > Not really. It isn't a member, it isn't a normal global
> > function, it's something completely magical, a friend.
>
> Before your messages in this thread, I had understood that "friend" was a
> relationship between a class and a function, rather than a special category
> of function.

Actually, it's both.

> But you're saying here that a friend function is a special kind
> of function, not a non-member function which has been granted special
> privileges.

Again, it's both a non-member function with privileges and
a special kind of function.

> I thought that a non-member function that was not a friend would work. But
> it now seems that a friend is required.

Correct

> It still seems to me that someone could make const_iterator be a non-nested
> class with a library-hidden name and just have the typedef inside the class.

Yes, why not.

> On the other hand, if only a friend function works, it still seems that my
> DR was correct.

Hum, hum

[quotation:
> However, the following code is not
> guaranteed to work:
>
>     bool check_equal(std::deque<int>::iterator i,
>         std::deque<int>::const_iterator ci)
>     {
>         return i == ci;
>     }
>
> The reason for this is that there's no requirement that the == operator be
> defined as a non-member function.
--end quotation]

> Here's what I'd like to know:
>
>     Do you still think the DR solution I proposed is incorrect?

As you seem to imply in your DR that a non friend function
solves the problem, yes. If all you wanted to say in your DR
was that there is problem which should be solved the Right
Way, then I have no objection against that.

>     If the standard is amended to require a non-member function, but the
> only kind of non-member function that will work is a friend, doesn't that
> mean that all implementers will have to use friend functions? Does the
> standard have to explicitly mention "friend" to be correct?

No, it doesn't has to do that. The standard doesn't even have to
mention the notion of membership of overloaded operators.

>     Would a simple wording change make my DR solution correct, corresponding
> to the experiment I did on my copy of the library?

Remove all the references to membership ?

>     Will you help me correct the DR and resubmit it?

Yes

> I don't care so much if I made a mistake -- I'm willing to admit it if I
> did. But I do care whether the DR is wrong or right. I'd like the DR to have
> a good chance for fair consideration, unless there's a flaw in it.

I am sure it will get fair consideration.

> I believe your original comment, saying that my suggested solution won't
> work, was passed on to the committee.

Correct

> I think this will unnecessarily reduce
> the chance of the defect report being accepted.

It will reduce the chance of the DR being accepted in
an incorrect/misleading form. It won't kill the DR.

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/23
Raw View
Doug Harrison wrote:

> P.S. I'm still curious as to how defining these functions as non-members
> "goes against the spirit of generic programming".

Using non members operators of course doesn't go against
the spirit of generic programming.

I have never said that.

(And I often use non member operators myself.)

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/19
Raw View
Darin Adler wrote:

> I meant to propose a non-member function, which can be a friend if
> necessary. As I understand it, the friend function above *is* a non-member
> function.

Not really. It isn't a member, it isn't a normal global
function, it's something completely magical, a friend.

> Can't a non-member function be a friend function?

Of course it can.

I just wanted to point out that:
- you did something (you changed your library code)
- you wrote some other thing in the DR, which makes
  your DR proposed solution incorrect

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Darin Adler" <darin@bentspoon.com>
Date: 1999/08/20
Raw View
> Darin Adler wrote:
>> I meant to propose a non-member function, which can be a friend if
>> necessary. As I understand it, the friend function above *is* a non-member
>> function.

Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote:
> Not really. It isn't a member, it isn't a normal global
> function, it's something completely magical, a friend.

Before your messages in this thread, I had understood that "friend" was a
relationship between a class and a function, rather than a special category
of function. But you're saying here that a friend function is a special kind
of function, not a non-member function which has been granted special
privileges.

I thought that a non-member function that was not a friend would work. But
it now seems that a friend is required.

Original code:

        bool operator ==(const const_iterator& rhs) const
        {return cur_ == rhs.cur_;}

Friend function:

        friend bool operator ==(const const_iterator& lhs,
            const const_iterator& rhs)
        {return lhs.cur_ == rhs.cur_;}

Non-friend function attempt:

        bool _equal(const const_iterator& rhs) const
        {return cur_ == rhs.cur_;}

    [...]

    template <class T>
    bool operator==(const list<T>::const_iterator& lhs,
        const list<T>::const_iterator& rhs)
    { return lhs._equal(rhs); }

But argument deduction won't work on the above attempt. Perhaps this is what
you originally meant when you said that deduction won't work.

It still seems to me that someone could make const_iterator be a non-nested
class with a library-hidden name and just have the typedef inside the class.
Like this:

    template <class T, class Allocator>
    class _list_const_iterator {
        [...]
        bool _equal(const _list_const_iterator & rhs) const
        {return cur_ == rhs.cur_;}
        [...]
    };

    template <class T, class Allocator>
    class list {
        [...]
        typedef _list_const_iterator<T, Allocator> const_iterator;
        [...]
    };

    template <class T, class Allocator>
    bool operator==(const _list_const_iterator<T, Allocator>& lhs,
        const _list_const_iterator<T, Allocator>& rhs)
    { return lhs._equal(rhs); }

With this design the operator== could be a standard non-member function
template. So it seems that there's no reason to demand a friend function.

On the other hand, if only a friend function works, it still seems that my
DR was correct. Because the standard would demand a non-member function that
works, and if the only kind that would work is a friend, then implementers
will have to use a friend. If an implementer discovers a way to make it work
properly without using a friend function, that's fine as well!

>> Can't a non-member function be a friend function?
>
> Of course it can.
>
> I just wanted to point out that:
> - you did something (you changed your library code)
> - you wrote some other thing in the DR, which makes
>   your DR proposed solution incorrect

Here's what I'd like to know:

    Do you still think the DR solution I proposed is incorrect?

    If so, why?

    If the standard is amended to require a non-member function, but the
only kind of non-member function that will work is a friend, doesn't that
mean that all implementers will have to use friend functions? Does the
standard have to explicitly mention "friend" to be correct?

    Would a simple wording change make my DR solution correct, corresponding
to the experiment I did on my copy of the library?

    Will you help me correct the DR and resubmit it?

I don't care so much if I made a mistake -- I'm willing to admit it if I
did. But I do care whether the DR is wrong or right. I'd like the DR to have
a good chance for fair consideration, unless there's a flaw in it. And if
there's a way I can amend it to be correct, I'd like to do that.

I believe your original comment, saying that my suggested solution won't
work, was passed on to the committee. I think this will unnecessarily reduce
the chance of the defect report being accepted. I hope the committee gets to
see our ensuing discussion as well.

    -- Darin
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Darin Adler" <darin@bentspoon.com>
Date: 1999/08/20
Raw View
> Darin Adler wrote:
>> I meant to propose a non-member function, which can be a friend if
>> necessary. As I understand it, the friend function above *is* a non-member
>> function.

Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote:
> Not really. It isn't a member, it isn't a normal global
> function, it's something completely magical, a friend.

Before your messages in this thread, I had understood that "friend" was a
relationship between a class and a function, rather than a special category
of function. But you're saying here that a friend function is a special kind
of function, not a non-member function which has been granted special
privileges.

I thought that a non-member function that was not a friend would work. But
it now seems that a friend is required.

Original code:

        bool operator ==(const const_iterator& rhs) const
        {return cur_ == rhs.cur_;}

Friend function:

        friend bool operator ==(const const_iterator& lhs,
            const const_iterator& rhs)
        {return lhs.cur_ == rhs.cur_;}

Non-friend function attempt:

        bool _equal(const const_iterator& rhs) const
        {return cur_ == rhs.cur_;}

    [...]

    template <class T>
    bool operator==(const list<T>::const_iterator& lhs,
        const list<T>::const_iterator& rhs)
    { return lhs._equal(rhs); }

But argument deduction won't work on the above attempt. Perhaps this is what
you originally meant when you said that deduction won't work.

It still seems to me that someone could make const_iterator be a non-nested
class with a library-hidden name and just have the typedef inside the class.
Like this:

    template <class T, class Allocator>
    class _list_const_iterator {
        [...]
        bool _equal(const _list_const_iterator & rhs) const
        {return cur_ == rhs.cur_;}
        [...]
    };

    template <class T, class Allocator>
    class list {
        [...]
        typedef _list_const_iterator<T, Allocator> const_iterator;
        [...]
    };

    template <class T, class Allocator>
    bool operator==(const _list_const_iterator<T, Allocator>& lhs,
        const _list_const_iterator<T, Allocator>& rhs)
    { return lhs._equal(rhs); }

With this design the operator== could be a standard non-member function
template. So it seems that there's no reason to demand a friend function.

On the other hand, if only a friend function works, it still seems that my
DR was correct. Because the standard would demand a non-member function that
works, and if the only kind that would work is a friend, then implementers
will have to use a friend. If an implementer discovers a way to make it work
properly without using a friend function, that's fine as well!

>> Can't a non-member function be a friend function?
>
> Of course it can.
>
> I just wanted to point out that:
> - you did something (you changed your library code)
> - you wrote some other thing in the DR, which makes
>   your DR proposed solution incorrect

Here's what I'd like to know:

    Do you still think the DR solution I proposed is incorrect?

    If so, why?

    If the standard is amended to require a non-member function, but the
only kind of non-member function that will work is a friend, doesn't that
mean that all implementers will have to use friend functions? Does the
standard have to explicitly mention "friend" to be correct?

    Would a simple wording change make my DR solution correct, corresponding
to the experiment I did on my copy of the library?

    Will you help me correct the DR and resubmit it?

I don't care so much if I made a mistake -- I'm willing to admit it if I
did. But I do care whether the DR is wrong or right. I'd like the DR to have
a good chance for fair consideration, unless there's a flaw in it. And if
there's a way I can amend it to be correct, I'd like to do that.

I believe your original comment, saying that my suggested solution won't
work, was passed on to the committee. I think this will unnecessarily reduce
the chance of the defect report being accepted. I hope the committee gets to
see our ensuing discussion as well.

    -- Darin
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1999/08/20
Raw View
Valentin Bonnard wrote:

>Doug Harrison wrote:
>>
>> Valentin Bonnard wrote:
>
>> >> The reason for this is that there's no requirement that the == operator be
>> >> defined as a non-member function.
>> >
>> >It's the contrary: as a non member function, deduction
>> >won't ever work.
>>
>> Who says std::deque::iterator has to be a member class?
>
>No one

Well, you seemed to be implying it, when you said:

>> >It's the contrary: as a non member function, deduction
>> >won't ever work.

in objection to this:

>     bool check_equal(std::deque<int>::iterator i,
>         std::deque<int>::const_iterator ci)
>     {
>         return i == ci;
>     }

If iterator was a member class, and operator== was a non-member, then it
would be a template function taking parameters of type:

 std::deque<template args>::iterator

and the template args wouldn't be deduced, per 14.8.2.4/4. At least I
thought that was what you were getting at.

>> it
>> certainly helps the library implementer provide symmetric operator==.

>???

iterator i;
const_iterator ci;

ci == i; // OK if operator== is a member
i == ci; // Not OK in typical implementations

The usual way to enable i == ci is to define the functions as non-members,
relying on conversion of the first argument from iterator to const_iterator:

template<params> bool operator==(iterator<params>,iterator<params>);
template<params> bool
   operator==(const_iterator<params>,const_iterator<params>);

Unless I'm misunderstanding something, making the iterator a nested class of
the deque template precludes defining operator== as a non-member, because of
deduction problems, while you can define them as non-members if iterator is
not a nested class.

On further thought, if iterator is derived from const_iterator, you may be
able to implement the symmetric behavior using overloaded member functions.

>> >So mandating non member functions certainly:
>> >- isn't a solution
>>
>> I think it's a solution for operator== et. al.
>
>No

Why?

>> Note that deque::iterator is
>> a random access iterator and thus has to support symmetric operator+ anyway.
>> This requires a non-member operator+ so you can say "n+i", where n is
>> difference_type and i is iterator.
>
>Completely unrelated and irrelevant

Why?

It's both related and relevant because if you have to define operator+ as a
non-member, and I'm right about the deduction problem, the implication is
that the iterator cannot be a nested class of the container, and then
there's no reason not to define operator== et. al. as non-members, solving
the original poster's problem. You might be interested to learn that neither
the SGI STL nor Dinkumware STL allows one to say n+i as described in my
earlier message. Reason: Both define these functions as members. In
addition, Dinkumware defines the iterators as nested classes.

P.S. I'm still curious as to how defining these functions as non-members
"goes against the spirit of generic programming".

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Bill Wade" <bill.wade@stoner.com>
Date: 1999/08/18
Raw View

Valentin Bonnard wrote in message <37BAC0EC.7C43@wanadoo.fr>...
>
>Bill Wade wrote:
>
>> 2) class iterator: public const_iterator { ... };    // inherit
operator==()
>
>At any rate, no. An iterator IS-NOT-A const iterator.


If it makes you feel better use
  class iterator: const_iterator { public: using const_iterator::operator==;
... };

However I'd argue that iterator IS-very-close-to-being-A const_iterator.

Almost anything you can do with a const iterator you can also do with an
iterator.  The exceptions involve areas like assignment and multi-level
pointers which are often violated by otherwise IS-A relationships.




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/19
Raw View
Doug Harrison wrote:
>
> Valentin Bonnard wrote:

> >> The reason for this is that there's no requirement that the == operator be
> >> defined as a non-member function.
> >
> >It's the contrary: as a non member function, deduction
> >won't ever work.
>
> Who says std::deque::iterator has to be a member class?

No one

> it
> certainly helps the library implementer provide symmetric operator==.

???

> >So mandating non member functions certainly:
> >- isn't a solution
>
> I think it's a solution for operator== et. al.

No

> Note that deque::iterator is
> a random access iterator and thus has to support symmetric operator+ anyway.
> This requires a non-member operator+ so you can say "n+i", where n is
> difference_type and i is iterator.

Completely unrelated and irrelevant

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Daniel Parker" <dparker@no_spam.globalserve.net>
Date: 1999/08/19
Raw View
Darin Adler <darin@bentspoon.com> wrote in message news:7pcr52
> With the standard STL idioms, there is no reason to convert between
> iterators and const_iterators. If you are operating on a modifiable
> container, you use iterators. If it's a const reference to the container,
> then you use const_iterators. Why, then, does the standard require a
> conversion from iterator to const_iterator for the container classes?
>
Consider

vector<double> u;

vector<double>::const_iterator iter = u.begin();

In this case, A.begin() returns an iterator, not a const_iterator (whether
the const or non-const version of begin is called depends on whether or not
the container u is const, not on the type of the left-hand argument.)

--
Regards,
Daniel Parker danielp@nshima.com
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Pete Becker <petebecker@acm.org>
Date: 1999/08/19
Raw View
Darin Adler wrote:
>
> With the standard STL idioms, there is no reason to convert between
> iterators and const_iterators. If you are operating on a modifiable
> container, you use iterators. If it's a const reference to the container,
> then you use const_iterators. Why, then, does the standard require a
> conversion from iterator to const_iterator for the container classes?
>

Because constness was not part of the original design of STL, but was
force-fit into it as part of the standardization process. (The original
STL did not have const_iterators) I'm not convinced that hacking around
with the requirements of operator== will fix the problems that STL has
with constness.

--
Pete Becker
Dinkumware, Ltd.
http://www.dinkumware.com
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Darin Adler" <darin@bentspoon.com>
Date: 1999/08/19
Raw View
>> Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote:
>> > It's the contrary: as a non member function, deduction
>> > won't ever work. With member functions, it will work
>> > sometimes (i == ci is an error, ci == i is ok).
>> >
>> > So mandating non member functions certainly:
>> > - isn't a solution

> Darin Adler wrote:
>> I tried this solution on my compiler before posting the defect report. I
>> changed the following function definition within the deque template in the
>> definition of deque::const_iterator:
>>
>>         bool operator ==(const const_iterator& rhs) const
>>         {return cur_ == rhs.cur_;}
>>
>> to this:
>>
>>         friend bool operator ==(const const_iterator& lhs,
>>             const const_iterator& rhs)
>>         {return lhs.cur_ == rhs.cur_;}

Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote:
> You didn't proposed a friend function in your DR.
> You proposed a non member function.

I meant to propose a non-member function, which can be a friend if
necessary. As I understand it, the friend function above *is* a non-member
function.

I didn't want to require that the function be a friend though. That's an
implementation detail that I didn't think needed to be constrained by the
standard.

Can't a non-member function be a friend function?

What am I missing here?

    -- Darin
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Pete Becker <petebecker@acm.org>
Date: 1999/08/19
Raw View
Valentin Bonnard wrote:
>
>
> ??? (re read my code example)
>

Explain your code example.

--
Pete Becker
Dinkumware, Ltd.
http://www.dinkumware.com


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Pete Becker <petebecker@acm.org>
Date: 1999/08/19
Raw View
blargg wrote:
>
> In article <37B9A195.B7090C3C@acm.org>, Pete Becker <petebecker@acm.org> wrote:
>
> > That way you don't have to worry about getting the type of the
> > iterator right. But the mistake in that particular loop is the decision
> > to use a const_iterator without regard to the actual constness of the
> > container.
>
> I suppose if you were given a pointer-to-non-const, you wouldn't consider
> ever converting that to a pointer-to-const?
>

Of course I would. Pointers are usable as STL iterators by necessity,
but it doesn't follow that everything you can do with a pointer is also
appropriate for an iterator.

> > Write it this way:
> >
> > for (Container::iterator it = cont.begin(); it != cont.end(); ++it)
> >         foo(*it);
>
> What if we only want to inspect the container, and enforce this decision
> by using a const iterator?
>

Constness admittedly doesn't fit well in STL -- it was added in after
the fact. But hacking around with modifications to operator== almost
certainly isn't the right solution. So I'll repeat my question, which
nobody so far has tried to address except by one-liners:

But the begin and end iterators that you get from a container
are of the same type, and they work just fine for the standard STL
idioms. Why do you need to do this? What coding practices does it
support?

--
Pete Becker
Dinkumware, Ltd.
http://www.dinkumware.com


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/08/19
Raw View
Bill Wade wrote:
>
> Darin Adler wrote in message <7p4nj8$n3u@enews4.newsguy.com>...
> >Proposed resolution:
> >
> >Add the following text to section 23.1 [lib.container.requirements]:
> >
> >The iterator and const_iterator types defined by a standard container may
> be
> >classes. If they are, any ==, !=, >, <, >=, or <= operator functions
> defined
> >for these classes to satisfy iterator requirements must be defined in the
> >std namespace, not as member functions.
> >
> >(Perhaps someone on the library committee can word it better. This is the
> >best I could do.)
>
> I'm not on the committee, but I believe the above is overly restrictive.
> Here are two ways that member functions can support the desired test ( i ==
> ci;    // iterator i;  const_iterator ci).
>
> 1) bool iterator::operator==(const_iterator) const;
> 2) class iterator: public const_iterator { ... };    // inherit operator==()
>
> Also note that binary minus should be added to the list of operators.  I
> believe that all iterators which support '<' also support '-'.

So perhaps instead sth. like the following should be added to the
standard:

 If i1 and i2 are iterators, then if any expression on one
 side of the following table is allowed, the operation on
 the other side shall be allowed, too, and have the same
 result:

 i1 == i2  |  i2 == i1
 i1 != i2  |  i2 != i1
 i1 > i2   |  i2 < i1
 i1 >= i2  |  i2 <= i1

I don't think this does constrain the implementation too
much, and it has exactly the intended effect.


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 1999/08/19
Raw View
On 18 Aug 1999 05:31:49 GMT, Darin Adler <darin@bentspoon.com> wrote:

>With the standard STL idioms, there is no reason to convert between
>iterators and const_iterators. If you are operating on a modifiable
>container, you use iterators. If it's a const reference to the container,
>then you use const_iterators. Why, then, does the standard require a
>conversion from iterator to const_iterator for the container classes?

A good question.  Anyway, if container<T>::iterator is "T *" and
container<T>::const_iterator is "T const *", then you get this
conversion whether you want it or not.  Maybe in order to make
the user-defined iterators like builtin pointers, they required
this rule -- but this is just my guess :).  I'd prefer if
vector<T>::iterator had to be a class.  This way it could have
typedefs, and you wouldn't have conversions from non-const to
const iterator.

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 1999/08/19
Raw View
On 18 Aug 1999 05:18:50 GMT, Bill Wade <bill.wade@stoner.com> wrote:
>Darin Adler wrote in message <7p4nj8$n3u@enews4.newsguy.com>...

>>If they are, any ==, !=, >, <, >=, or <= operator functions defined
>>for these classes to satisfy iterator requirements must be defined in the
>>std namespace, not as member functions.

>I'm not on the committee, but I believe the above is overly restrictive.
>Here are two ways that member functions can support the desired test ( i ==
>ci;    // iterator i;  const_iterator ci).
>
>1) bool iterator::operator==(const_iterator) const;
>2) class iterator: public const_iterator { ... };    // inherit operator==()

I must have missed something.  If the conversion from 'iterator' to
'const_iterator' is implicit, then we could also use a non-member
operator==(const_iterator,const_iterator), optionally a friend.  So
there is a choice 3) too, right?

I wrote my own iterator class iter<T>, and iterator is iter<T> and
const_iterator is iter<const T>.  To write seperate classes class
iter<T> and const_iter<T> is tedious.  So I don't like 1) or 2).
If I had an implicit conversion
   iter<T>::iter(const iter<U>&);
then choice 3) is feasible (eg, T==int and U==const int).  However,
for most type safety and best error messages, I don't want this
templated implicit constructor, so choice 3) I also don't like.

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Bill Wade" <bill.wade@stoner.com>
Date: 1999/08/18
Raw View

Darin Adler wrote in message <7p4nj8$n3u@enews4.newsguy.com>...
>Proposed resolution:
>
>Add the following text to section 23.1 [lib.container.requirements]:
>
>The iterator and const_iterator types defined by a standard container may
be
>classes. If they are, any ==, !=, >, <, >=, or <= operator functions
defined
>for these classes to satisfy iterator requirements must be defined in the
>std namespace, not as member functions.
>
>(Perhaps someone on the library committee can word it better. This is the
>best I could do.)


I'm not on the committee, but I believe the above is overly restrictive.
Here are two ways that member functions can support the desired test ( i ==
ci;    // iterator i;  const_iterator ci).

1) bool iterator::operator==(const_iterator) const;
2) class iterator: public const_iterator { ... };    // inherit operator==()

Also note that binary minus should be added to the list of operators.  I
believe that all iterators which support '<' also support '-'.



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Darin Adler" <darin@bentspoon.com>
Date: 1999/08/18
Raw View
Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote:

> [Note: fowarded to the issue list maintainer]
>
> Darin Adler wrote:
>
>> Some standard containers define iterator classes to fulfill the requirements
>> described in 23.1 [lib.container.requirements]. This states that iterator
>> must be convertible to const_iterator. However, the following code is not
>> guaranteed to work:
>>
>>     bool check_equal(std::deque<int>::iterator i,
>>         std::deque<int>::const_iterator ci)
>>     {
>>         return i == ci;
>>     }
>>
>> The reason for this is that there's no requirement that the == operator be
>> defined as a non-member function.
>
> It's the contrary: as a non member function, deduction
> won't ever work. With member functions, it will work
> sometimes (i == ci is an error, ci == i is ok).
>
> So mandating non member functions certainly:
> - isn't a solution

I tried this solution on my compiler before posting the defect report. I
changed the following function definition within the deque template in the
definition of deque::const_iterator:

        bool operator ==(const const_iterator& rhs) const
        {return cur_ == rhs.cur_;}

to this:

        friend bool operator ==(const const_iterator& lhs,
            const const_iterator& rhs)
        {return lhs.cur_ == rhs.cur_;}

After this change, everything worked as before, but the check_equal function
compiled and executed correctly.

In light of this, I don't understand what you mean when you say, "deduction
won't ever work."

Is there something non-standard about the compiler I'm using that is causing
it to work? Is there some other test case that will show me that deduction
isn't working?

> - goes agains the spirit of generic programming

I'd like to argue about this one, but since you offer no evidence of how it
goes against the spirit of generic programming, I don't know what to argue
with. It seems rather subjective.

I just want the library to work. I don't want to constrain implementers of
non-library classes, but I'd like the library classes to work consistently,
even with different implementations of the library.

    -- Darin


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Darin Adler" <darin@bentspoon.com>
Date: 1999/08/18
Raw View
Pete Becker <petebecker@acm.org> wrote:

> Agreed. But the begin and end iterators that you get from a container
> are of the same type, and they work just fine for the standard STL
> idioms. Why do you need to do this? What coding practices does it
> support?

This originally came to my attention because of a question another C++ user
asked me about why his code wasn't compiling. The code was otherwise quite
good and didn't go beyond the standard in other ways. I realized that some
compilers might accept the == comparison and others wouldn't. It seemed
unnecessary to leave this unspecified in the standard, since one approach
was clearly superior. As far as I can tell, in every other case of a class
defined in the standard library, the standard mandates non-member functions
for this kind of binary operator.

I encountered a similar problem when rolling out a checked implementation of
vector that I was experimenting with. When the vector iterators were defined
as instances of a class rather than as pointers, this == comparison problem
showed up in some user code. I fixed the problem by making operator == be a
friend function instead of a member function, but it revealed that there was
code lurking that didn't comply with the standard. I had a hard time
explaining why reversing the order of the operands to == would make the code
"correct" to my colleagues.

I don't know all the situations where this could come up, but a common one
is where you have a const_iterator and a container and want to compare it
against the beginning or end iterator of a container. (I agree that with the
typical STL idiom you would have the begin and end iterators, not the
container.) If you happen to have a non-const reference to the container,
the comparison only works in one direction. The opposite occurs if you have
a non-const iterator and a const reference to the container.

Other examples come up when you store a const_iterator, then need to compare
it against a parameter that's an iterator, or when you store an iterator and
need to compare it against a parameter that's a const_iterator. This doesn't
come up often with deque<>, since iterators are invalidated by so many
operations, but it does come up with list<>.

The real issue for me is not the need to write code like this, but the fact
that code like this will work on some implementations, but is not required
to work on other standard-conforming implementations. I think it's nice to
minimize unspecified cases like this in the standard if possible, unless
there's significant flexibility or clarity gained by leaving it unspecified.

For example, I'm glad that the standard does not allow operator+(const
string&, const string&) to be implemented as a member function of class
string, because it would otherwise be too easy to accidentally write code
that works on some implementations but not others.

Much of my work involves modifying and maintaining programs that other
people write. Thus, the issue affects me even if I never plan to write code
that uses iterators in this way. I'm often the first programmer to expose
existing code to a new compiler.

I'd like to counter with another question:

With the standard STL idioms, there is no reason to convert between
iterators and const_iterators. If you are operating on a modifiable
container, you use iterators. If it's a const reference to the container,
then you use const_iterators. Why, then, does the standard require a
conversion from iterator to const_iterator for the container classes?

    -- Darin


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/08/18
Raw View
In article <37B9A195.B7090C3C@acm.org>, Pete Becker <petebecker@acm.org> wrote:

> Valentin Bonnard wrote:
> >
> > Pete Becker wrote:
> >
> > > Darin Adler wrote:
> >
> > > >     bool check_equal(std::deque<int>::iterator i,
> > > >         std::deque<int>::const_iterator ci)
> > > >     {
> > > >         return i == ci;
> > > >     }
> > > >
> > >
> > > Agreed. But the begin and end iterators that you get from a container
> > > are of the same type, and they work just fine for the standard STL
> > > idioms. Why do you need to do this? What coding practices does it
> > > support?
> >
> > Would you consider the following unatural or silly ?
> >
> > for (const_iterator it = begin(); it!=end(); ++it)
> >     foo (*it);
>
> Absolutely. <g> The standard STL idiom is to put the loop in a template
> function.

Yeah. It's simpler for compiler writers to only allow one for loop per
function :-)

Ugh. What a mess, writing multiple functions like this. I'll pass.

> That way you don't have to worry about getting the type of the
> iterator right. But the mistake in that particular loop is the decision
> to use a const_iterator without regard to the actual constness of the
> container.

I suppose if you were given a pointer-to-non-const, you wouldn't consider
ever converting that to a pointer-to-const?

> Write it this way:
>
> for (Container::iterator it = cont.begin(); it != cont.end(); ++it)
>         foo(*it);

What if we only want to inspect the container, and enforce this decision
by using a const iterator?


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1999/08/18
Raw View
Valentin Bonnard wrote:

>>     bool check_equal(std::deque<int>::iterator i,
>>         std::deque<int>::const_iterator ci)
>>     {
>>         return i == ci;
>>     }
>>
>> The reason for this is that there's no requirement that the == operator be
>> defined as a non-member function.
>
>It's the contrary: as a non member function, deduction
>won't ever work.

Who says std::deque::iterator has to be a member class? It could be defined
as std::__deque_iterator, and deque::iterator could just be a typedef for
it. It may not help you portably write a templated check_equal, but it
certainly helps the library implementer provide symmetric operator==.

>So mandating non member functions certainly:
>- isn't a solution

I think it's a solution for operator== et. al. Note that deque::iterator is
a random access iterator and thus has to support symmetric operator+ anyway.
This requires a non-member operator+ so you can say "n+i", where n is
difference_type and i is iterator.

>- goes agains the spirit of generic programming

Why?

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/18
Raw View
Pete Becker wrote:

> Valentin Bonnard wrote:

> > Would you consider the following unatural or silly ?
> >
> > for (const_iterator it = begin(); it!=end(); ++it)
> >     foo (*it);
> >
>
> Absolutely. <g>

I was sure that you would say that.

> The standard STL idiom is to put the loop in a template
> function.

I wouldn't expect every one-use algorithm to be encapsulated in
a function. It's very tedious.

To use the algorithm you need to know by heart the combinators,
which are ugly and verbose.

> for (Container::iterator it = cont.begin(); it != cont.end(); ++it)
>         foo(*it);

??? (re read my code example)

--

Valentin Bonnard


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/18
Raw View
Darin Adler wrote:

> Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote:
> > It's the contrary: as a non member function, deduction
> > won't ever work. With member functions, it will work
> > sometimes (i == ci is an error, ci == i is ok).
> >
> > So mandating non member functions certainly:
> > - isn't a solution
>
> I tried this solution on my compiler before posting the defect report. I
> changed the following function definition within the deque template in the
> definition of deque::const_iterator:
>
>         bool operator ==(const const_iterator& rhs) const
>         {return cur_ == rhs.cur_;}
>
> to this:
>
>         friend bool operator ==(const const_iterator& lhs,
>             const const_iterator& rhs)
>         {return lhs.cur_ == rhs.cur_;}

You didn't proposed a friend function in your DR.
You proposed a non member function.

--

Valentin Bonnard


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/18
Raw View
Bill Wade wrote:

> 2) class iterator: public const_iterator { ... };    // inherit operator==()

At any rate, no. An iterator IS-NOT-A const iterator.

--

Valentin Bonnard


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Darin Adler" <darin@bentspoon.com>
Date: 1999/08/16
Raw View
[ moderator's note: forwarded to C++ committee for handling. -sdc ]

Some standard containers define iterator classes to fulfill the requirements
described in 23.1 [lib.container.requirements]. This states that iterator
must be convertible to const_iterator. However, the following code is not
guaranteed to work:

    bool check_equal(std::deque<int>::iterator i,
        std::deque<int>::const_iterator ci)
    {
        return i == ci;
    }

The reason for this is that there's no requirement that the == operator be
defined as a non-member function. The implementation is allowed to use a
member function, which prevents the case above from working. Conversions
can't be applied to the argument on the left side of the operator.

The iterator requirements in section 24.1 [lib.iterator.requirements] are
sufficient to define each iterator class on its own, but state nothing one
way or another about cases where there's a type conversion involved. The
standard should require that the iterators defined by the standard
collections go beyond the minimum iterator requirements in this way to
define unambiguously that the code above should work.

Proposed resolution:

Add the following text to section 23.1 [lib.container.requirements]:

The iterator and const_iterator types defined by a standard container may be
classes. If they are, any ==, !=, >, <, >=, or <= operator functions defined
for these classes to satisfy iterator requirements must be defined in the
std namespace, not as member functions.

(Perhaps someone on the library committee can word it better. This is the
best I could do.)

    -- Darin


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/17
Raw View
[Note: fowarded to the issue list maintainer]

Darin Adler wrote:

> Some standard containers define iterator classes to fulfill the requirements
> described in 23.1 [lib.container.requirements]. This states that iterator
> must be convertible to const_iterator. However, the following code is not
> guaranteed to work:
>
>     bool check_equal(std::deque<int>::iterator i,
>         std::deque<int>::const_iterator ci)
>     {
>         return i == ci;
>     }
>
> The reason for this is that there's no requirement that the == operator be
> defined as a non-member function.

It's the contrary: as a non member function, deduction
won't ever work. With member functions, it will work
sometimes (i == ci is an error, ci == i is ok).

So mandating non member functions certainly:
- isn't a solution
- goes agains the spirit of generic programming

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Pete Becker <petebecker@acm.org>
Date: 1999/08/17
Raw View
Darin Adler wrote:
>
> [ moderator's note: forwarded to C++ committee for handling. -sdc ]
>
> Some standard containers define iterator classes to fulfill the requirements
> described in 23.1 [lib.container.requirements]. This states that iterator
> must be convertible to const_iterator. However, the following code is not
> guaranteed to work:
>
>     bool check_equal(std::deque<int>::iterator i,
>         std::deque<int>::const_iterator ci)
>     {
>         return i == ci;
>     }
>

Agreed. But the begin and end iterators that you get from a container
are of the same type, and they work just fine for the standard STL
idioms. Why do you need to do this? What coding practices does it
support?

--
Pete Becker
Dinkumware, Ltd.
http://www.dinkumware.com
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/17
Raw View
Pete Becker wrote:

> Darin Adler wrote:

> >     bool check_equal(std::deque<int>::iterator i,
> >         std::deque<int>::const_iterator ci)
> >     {
> >         return i == ci;
> >     }
> >
>
> Agreed. But the begin and end iterators that you get from a container
> are of the same type, and they work just fine for the standard STL
> idioms. Why do you need to do this? What coding practices does it
> support?

Would you consider the following unatural or silly ?

for (const_iterator it = begin(); it!=end(); ++it)
    foo (*it);

Being able to write a for loop is a standard C++ idiom.

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Pete Becker <petebecker@acm.org>
Date: 1999/08/18
Raw View
Valentin Bonnard wrote:
>
> Pete Becker wrote:
>
> > Darin Adler wrote:
>
> > >     bool check_equal(std::deque<int>::iterator i,
> > >         std::deque<int>::const_iterator ci)
> > >     {
> > >         return i == ci;
> > >     }
> > >
> >
> > Agreed. But the begin and end iterators that you get from a container
> > are of the same type, and they work just fine for the standard STL
> > idioms. Why do you need to do this? What coding practices does it
> > support?
>
> Would you consider the following unatural or silly ?
>
> for (const_iterator it = begin(); it!=end(); ++it)
>     foo (*it);
>

Absolutely. <g> The standard STL idiom is to put the loop in a template
function. That way you don't have to worry about getting the type of the
iterator right. But the mistake in that particular loop is the decision
to use a const_iterator without regard to the actual constness of the
container. Write it this way:

for (Container::iterator it = cont.begin(); it != cont.end(); ++it)
 foo(*it);

--
Pete Becker
Dinkumware, Ltd.
http://www.dinkumware.com


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]