Topic: What should std::InputIterator<T>::pointer be? [n2193 / n2083]


Author: "Richard Smith" <richard@ex-parrot.com>
Date: Wed, 4 Apr 2007 10:00:21 CST
Raw View
[I sent a similar reply to this on Monday, but it hasn't yet arrived.
Apologies if both now arrive.]

On 1 Apr, 20:52, nore...@this.is.invalid (Niels Dekker - no return
address) wrote:
> Richard Smith wrote:
> > It's standard practice to implement operator-> on such iterators by
> > way of a proxy object in order to extend the lifetime of the
> > temporary:
> >   class my_iterator {
> >     struct arrow_proxy {  [...]
> >     };
> >   public:
> >     arrow_proxy operator->() const { return **this; }
> >   };
>
> > However, it is also current practice to make the 'pointer' typedef a
> > raw pointer (see the boost::iterator_facade and elsewhere in Boost for
> > examples):
> >   typedef value_type const* pointer;
>
> Even the current (pre-concept) draft of the Standard explicitly
> specifies 'pointer' to be the return type of operator->():
>     iterator_traits<Iterator>::reference
>     iterator_traits<Iterator>::pointer
>   shall be defined as the iterator's reference and pointer types,
>   that is, for an iterator object a, the same type as the type of
>   *a and a->, respectively.http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2135.pdf
> (Iterator traits, section 24.3.1)

Excellent!  I hadn't noticed this change.  (It would be nice if the
working draft of the standard could identify changes, say with a
sidebar, but I'm sure maintaining the working draft is enough of a
problem without this.)


> So I think that Boost's iterator_facade is slightly outdated, as the
> return type of iterator_facade::operator->() might be different from
> iterator_facade::pointer.http://www.boost.org/boost/iterator/iterator_facade.hpp

And with Boost, I'm sure they'll fix it in plenty of time for C++0x;
the concern is more with other libraries.  But as the working in
24.3.1 makes it undefined behaviour for pointer not to be the return
type of operator->, I hope that, as a QoI issue, implementations will
catch this and give a compile-time error, for example with

  concept InputIterator <typename X> {
    requires SameType<
      decltype( static_cast<X*>(nullptr)->operator->() ),
      pointer
    >;
  }

> > As arrow_type is convertible to pointer, my_iterator::operator->()
> > will satisfy this associated function requirement, but concept-based
> > code will now see an operator-> that returns a raw pointer.
>
> >   concept foo<typename T> { void T::bar(); };
>
> >   template < std::InputIterator Iter >
> >     requires std::SameType<Iter::value_type, foo>
> >   void fn Iter i ) { i->bar(); }
>
> > I'm not entirely sure I've got this correct syntacticaly
>
> I guess you mean:
>   template < std::InputIterator Iter >
>     requires foo<Iter::value_type>
>   void fn( Iter i ) { i->bar(); }

Yes.  Sorry --  I changed foo from a struct to a concept part way
through writing this email.

[...]

> If it still does not compile on ConceptGCC, it's probably a compiler
> bug!  And indeed, ConceptGCC 4.1.1 alpha 5 says:
>   error: result of 'operator->()' yields non-pointer result

Actually, I think this is more serious -- I think ConceptGCC is
correct to give an error.  Specifically, I think the following
translation unit is erroneous:

  #include <iterator>

  concept Foo<typename T> {
    void T::bar();
  };

  template < std::InputIterator Iter >
    requires Foo<Iter::value_type>
  void fn( Iter i ) { i->bar(); }

. even though fn() is never instantiated.

The problem is that InputIterator::operator->() returns a 'pointer'
type, which is an opaque type without any same-type requirements.
(The requirement that it can convert to value_type const* is not
relevant here.)  According to 13.5.6, "x->m is interpreted as
(x.operator->())->m for a class object x".  This means i->bar() is
interpreted as

  i.std::InputIterator<Iter>::operator->()->bar

which leaves the compiler resolving operator-> on an opaque type,
'pointer'.  In a constrained template, this needs to be done at parse
time (rather than at instantiation time).  As operator-> can't be a
non-member template (even in n2135), it can only be found via a
concept, but no concept requirements are given for pointer.

  auto concept MemberAccessProxy<typename Proxy, typename Pointer>
    : CopyConstructible<Proxy>  // or Movable?
  {
    requires Convertible<Proxy, Pointer>;
    Pointer operator->(Proxy) const;
  }

  concept InputIterator<typename X> {
    requires MemberAccessProxy<pointer, value_type const*>;
  }

Except this won't work as MutableInputIterator will need

  requires MemberAccessProxy<pointer, value_type*>;

and this (I think) will lead to an ambiguous overload for
pointer::operator-> in mutable iterators.

--
Richard Smith

---
[ 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://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Douglas Gregor" <doug.gregor@gmail.com>
Date: Wed, 4 Apr 2007 10:11:23 CST
Raw View
On Apr 1, 3:52 pm, nore...@this.is.invalid (Niels Dekker - no return
address) wrote:
> Richard Smith wrote:
> > However, it is also current practice to make the 'pointer' typedef a
> > raw pointer (see the boost::iterator_facade and elsewhere in Boost for
> > examples):
> >   typedef value_type const* pointer;
>
> Even the current (pre-concept) draft of the Standard explicitly
> specifies 'pointer' to be the return type of operator->():
>     iterator_traits<Iterator>::reference
>     iterator_traits<Iterator>::pointer
>   shall be defined as the iterator's reference and pointer types,
>   that is, for an iterator object a, the same type as the type of
>   *a and a->, respectively.http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2135.pdf
> (Iterator traits, section 24.3.1)

I had forgotten that we fixed this in C++0x, but I'm glad to see it
there :)

> So I think that Boost's iterator_facade is slightly outdated, as the
> return type of iterator_facade::operator->() might be different from
> iterator_facade::pointer.http://www.boost.org/boost/iterator/iterator_facade.hpp

Yes, this appears to be the case.

> > Have I understood this correctly?  If so, is there some way of around
> > this that doesn't require changing the 'pointer' typedef in all
> > existing code?
>
> Yes, by writing your own concept map, that redefines both 'pointer' and
> operator-> for my_iterator. For instance by using operator_arrow_proxy
> from boost/iterator/iterator_facade.hpp, as follows:
>
> namespace std {
>   concept_map InputIterator<my_iterator> {
>     typedef boost::detail::operator_arrow_proxy<value_type> pointer;
>     pointer operator->(const my_iterator& X) {
>       pointer result( X.operator->() );
>       return result;
>
> } } }

Yes, this is one solution, but there is still a backward-compatibility
problem here. There are lots of iterators in the world that use
proxies but define the "pointer" type in iterator_traits to "const
value_type *". When the C++ committee "fixed" the specification of the
pointer and reference types, it made all of this code ill-formed:
however, since nobody actually uses the "pointer" type, it hasn't
really mattered. When concepts start using this type, we're going to
get failures. Yes, the code was wrong, but it represents a porting and
backward compatibility problem.

I think that there is a way out. Existing C++98/03 iterators provide
iterator_traits, but they do not provide concept maps for the iterator
concepts (e.g., InputIterator). The C++0x standard library provides
backward-compatibility concept maps that turn C++98/03 iterators into C
++0x iterators, by querying iterator_traits. Right now, these backward-
compatibility concept maps rely on the pointer and reference types in
iterator_traits being correct. However, one could let the concept
mechanism deduce "pointer" (and perhaps "reference"), ignoring what is
in iterator_traits. I expect this is a quality-of-implementation
issue, because we're talking about making ill-formed code that used to
work still work.

> If it still does not compile on ConceptGCC, it's probably a compiler
> bug!  And indeed, ConceptGCC 4.1.1 alpha 5 says:
>   error: result of 'operator->()' yields non-pointer result
> Douglas Gregor told me that the current version of ConceptGCC does not
> preserve the proxy as long as it should, he just didn't have the time to
> fix it yet.

Yes, ConceptGCC gets operator-> very, very wrong.

  Cheers,
  Doug

---
[ 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://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Richard Smith" <richard@ex-parrot.com>
Date: Fri, 30 Mar 2007 16:48:58 CST
Raw View
It is not unusual to encounter iterators whose operator* returns by
value (c.f. boost::iterator_facade). They satisfy the current (C++03)
InputIterator concept, and it seems that the intention is that these
can satisfy any of the new (C++09) immutable iterator concepts,
including InputIterator.

It's standard practice to implement operator-> on such iterators by
way of a proxy object in order to extend the lifetime of the
temporary:

  class my_iterator {
    struct arrow_proxy {
      arrow_proxy( value_type const& val ) : val(val) {}
      operator value_type const*() const { return &val; }
      value_type const* operator->() const { return &val; }
    private:
      value_type val;
    };
  public:
    arrow_proxy operator->() const { return **this; }
  };

However, it is also current practice to make the 'pointer' typedef a
raw pointer (see the boost::iterator_facade and elsewhere in Boost for
examples):

  typedef value_type const* pointer;

Assuming 'my_iterator' has is otherwise a plausible-looking iterator,
the library will supply a concept map to turn this into a
std::InputIterator (per n2083: 24.3.5/4), and this includes a
function.

  concept InputIterator<typename X> {
    pointer operator->(X);
  };

As arrow_type is convertible to pointer, my_iterator::operator->()
will satisfy this associated function requirement, but concept-based
code will now see an operator-> that returns a raw pointer.

  concept foo<typename T> { void T::bar(); };

  template < std::InputIterator Iter >
    requires std::SameType<Iter::value_type, foo>
  void fn Iter i ) { i->bar(); }

I'm not entirely sure I've got this correct syntacticaly, as
ConceptGCC doesn't like it, even when 'requires' is replaced with
'where'.)  But assuming this is legal modulo any syntactic snafus,
does it cause undefined behaviour when used with my_iterator<foo>?
I.e. does it call foo::bar on a foo object after it has been
destroyed?  So far as I can tell, the answer is 'yes'.

If so, this is a problem -- suppose fn is in a third party library
that has recently been upgrade to be concept-aware, but my_iterator is
an older non-concept-aware library: both libraries seem 'correct' by
themselves, but in combination the previously-valid code in
my_iterator is causing problems.

Have I understood this correctly?  If so, is there some way of around
this that doesn't require changing the 'pointer' typedef in all
existing code?

--
Richard Smith

---
[ 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://www.comeaucomputing.com/csc/faq.html                      ]





Author: noreply@this.is.invalid (Niels Dekker - no return address)
Date: Sun, 1 Apr 2007 19:52:33 GMT
Raw View
Richard Smith wrote:
> It's standard practice to implement operator-> on such iterators by
> way of a proxy object in order to extend the lifetime of the
> temporary:
>   class my_iterator {
>     struct arrow_proxy {  [...]
>     };
>   public:
>     arrow_proxy operator->() const { return **this; }
>   };
>=20
> However, it is also current practice to make the 'pointer' typedef a
> raw pointer (see the boost::iterator_facade and elsewhere in Boost for
> examples):
>   typedef value_type const* pointer;

Even the current (pre-concept) draft of the Standard explicitly
specifies 'pointer' to be the return type of operator->():
    iterator_traits<Iterator>::reference
    iterator_traits<Iterator>::pointer
  shall be defined as the iterator=92s reference and pointer types,
  that is, for an iterator object a, the same type as the type of
  *a and a->, respectively.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2135.pdf
(Iterator traits, section 24.3.1)

See also DR #445, by David Abrahams, 2003-12-09:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#445

So I think that Boost's iterator_facade is slightly outdated, as the
return type of iterator_facade::operator->() might be different from
iterator_facade::pointer.
http://www.boost.org/boost/iterator/iterator_facade.hpp


> As arrow_type is convertible to pointer, my_iterator::operator->()
> will satisfy this associated function requirement, but concept-based
> code will now see an operator-> that returns a raw pointer.
>=20
>   concept foo<typename T> { void T::bar(); };
>=20
>   template < std::InputIterator Iter >
>     requires std::SameType<Iter::value_type, foo>
>   void fn Iter i ) { i->bar(); }
>=20
> I'm not entirely sure I've got this correct syntacticaly

I guess you mean:
  template < std::InputIterator Iter >
    requires foo<Iter::value_type>
  void fn( Iter i ) { i->bar(); }

> But assuming this is legal modulo any syntactic snafus,
> does it cause undefined behaviour when used with my_iterator<foo>?
> I.e. does it call foo::bar on a foo object after it has been
> destroyed?  So far as I can tell, the answer is 'yes'.

Yes, it does.  So there's a serious problem if my_iterator::pointer is
not the return type of my_iterator::operator->(). =20

> Have I understood this correctly?  If so, is there some way of around
> this that doesn't require changing the 'pointer' typedef in all
> existing code?

Yes, by writing your own concept map, that redefines both 'pointer' and
operator-> for my_iterator. For instance by using operator_arrow_proxy
from boost/iterator/iterator_facade.hpp, as follows:

namespace std {
  concept_map InputIterator<my_iterator> {
    typedef boost::detail::operator_arrow_proxy<value_type> pointer;
    pointer operator->(const my_iterator& X) {
      pointer result( X.operator->() );
      return result;=20
} } }

If it still does not compile on ConceptGCC, it's probably a compiler
bug!  And indeed, ConceptGCC 4.1.1 alpha 5 says:
  error: result of 'operator->()' yields non-pointer result
Douglas Gregor told me that the current version of ConceptGCC does not
preserve the proxy as long as it should, he just didn't have the time to
fix it yet.

--=20
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
C++ programmer at LKEB, Leiden University Medical Center

---
[ 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://www.comeaucomputing.com/csc/faq.html                      ]





Author: Carl Barron <cbarron413@adelphia.net>
Date: Mon, 2 Apr 2007 14:48:08 CST
Raw View
In article <1175280502.400506.152680@e65g2000hsc.googlegroups.com>,
Richard Smith <richard@ex-parrot.com> wrote:

> It is not unusual to encounter iterators whose operator* returns by
> value (c.f. boost::iterator_facade). They satisfy the current (C++03)
> InputIterator concept, and it seems that the intention is that these
> can satisfy any of the new (C++09) immutable iterator concepts,
> including InputIterator.
>
> It's standard practice to implement operator-> on such iterators by
> way of a proxy object in order to extend the lifetime of the
> temporary:
>
>   class my_iterator {
>     struct arrow_proxy {
>       arrow_proxy( value_type const& val ) : val(val) {}
>       operator value_type const*() const { return &val; }
>       value_type const* operator->() const { return &val; }
>     private:
>       value_type val;
>     };
>   public:
>     arrow_proxy operator->() const { return **this; }
>   };
>
> However, it is also current practice to make the 'pointer' typedef a
> raw pointer (see the boost::iterator_facade and elsewhere in Boost for
> examples):
>
   my usual inplimentation ,if the provided proxy does not work, is
something like this:

    class my_iterator:boost::iterator_facade<...>
    {
    public:
      Foo operator ->();
      // ...
    };

   All is fine as my_iterator::operator->() will be called not
iterator_facade<...>::operator->().

---
[ 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://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Richard Smith" <richard@ex-parrot.com>
Date: Mon, 2 Apr 2007 18:28:23 CST
Raw View
On 2 Apr, 21:48, Carl Barron <cbarron...@adelphia.net> wrote:
>
>    my usual inplimentation ,if the provided proxy does not work, is
> something like this:
>
>     class my_iterator:boost::iterator_facade<...>
>     {
>     public:
>       Foo operator ->();
>       // ...
>     };
>
>    All is fine as my_iterator::operator->() will be called not
> iterator_facade<...>::operator->().

I think you fundamentally misunderstand my point --
boost::iterator_facade, and its current operator-> proxy, works well
with today's standard.  My point is that it won't work with C++09 if
the current concepts proposal is included.  But the problem isn't with
the proxy type, it's with the definition of the 'pointer' type... or
maybe with the meaning the standard ascribes to it.

--
Richard Smith

---
[ 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://www.comeaucomputing.com/csc/faq.html                      ]