659. istreambuf_iterator should have an operator->()

Section: 27.6.3 [istreambuf.iterator] Status: C++11 Submitter: Niels Dekker Opened: 2007-03-25 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [istreambuf.iterator].

View all issues with C++11 status.

Discussion:

Greg Herlihy has clearly demonstrated that a user defined input iterator should have an operator->(), even if its value type is a built-in type (comp.std.c++, "Re: Should any iterator have an operator->() in C++0x?", March 2007). And as Howard Hinnant remarked in the same thread that the input iterator istreambuf_iterator doesn't have one, this must be a defect!

Based on Greg's example, the following code demonstrates the issue:

 #include <iostream> 
 #include <fstream>
 #include <streambuf> 

 typedef char C;
 int main ()
 {
   std::ifstream s("filename", std::ios::in);
   std::istreambuf_iterator<char> i(s);

   (*i).~C();  // This is well-formed...
   i->~C();  // ... so this should be supported!
 }

Of course, operator-> is also needed when the value_type of istreambuf_iterator is a class.

The operator-> could be implemented in various ways. For instance, by storing the current value inside the iterator, and returning its address. Or by returning a proxy, like operator_arrow_proxy, from http://www.boost.org/boost/iterator/iterator_facade.hpp

I hope that the resolution of this issue will contribute to getting a clear and consistent definition of iterator concepts.

[ Kona (2007): The proposed resolution is inconsistent because the return type of istreambuf_iterator::operator->() is specified to be pointer, but the proposed text also states that "operator-> may return a proxy." ]

[ Niels Dekker (mailed to Howard Hinnant): ]

The proposed resolution does not seem inconsistent to me. istreambuf_iterator::operator->() should have istreambuf_iterator::pointer as return type, and this return type may in fact be a proxy.

AFAIK, the resolution of 445 ("iterator_traits::reference unspecified for some iterator categories") implies that for any iterator class Iter, the return type of operator->() is Iter::pointer, by definition. I don't think Iter::pointer needs to be a raw pointer.

Still I wouldn't mind if the text "operator-> may return a proxy" would be removed from the resolution. I think it's up to the library implementation, how to implement istreambuf_iterator::operator->(). As longs as it behaves as expected: i->m should have the same effect as (*i).m. Even for an explicit destructor call, i->~C(). The main issue is just: istreambuf_iterator should have an operator->()!

[ 2009-04-30 Alisdair adds: ]

Note that operator-> is now a requirement in the InputIterator concept, so this issue cannot be ignored or existing valid programs will break when compiled with an 0x library.

[ 2009-05-29 Alisdair adds: ]

I agree with the observation that in principle the type 'pointer' may be a proxy, and the words highlighting this are redundant.

However, in the current draught pointer is required to be exactly 'charT *' by the derivation from std::iterator. At a minimum, the 4th parameter of this base class template should become unspecified. That permits the introduction of a proxy as a nested class in some further undocumented (not even exposition-only) base.

It also permits the istream_iterator approach where the cached value is stored in the iterator itself, and the iterator serves as its own proxy for post-increment operator++ - removing the need for the existing exposition-only nested class proxy.

Note that the current proxy class also has exactly the right properties to serve as the pointer proxy too. This is likely to be a common case where an InputIterator does not hold internal state but delegates to another class.

Proposed Resolution:

In addition to the current proposal:

27.6.3 [istreambuf.iterator]

template<class charT, class traits = char_traits<charT> >
class istreambuf_iterator
  : public iterator<input_iterator_tag, charT,
                    typename traits::off_type, charT* unspecified, charT> {

[ 2009-07 Frankfurt ]

Move the additional part into the proposed resolution, and wrap the descriptive text in a Note.

[Howard: done.]

Move to Ready.

Proposed resolution:

Add to the synopsis in 27.6.3 [istreambuf.iterator]:

charT operator*() const;
pointer operator->() const;
istreambuf_iterator<charT,traits>& operator++();

27.6.3 [istreambuf.iterator]

template<class charT, class traits = char_traits<charT> >
class istreambuf_iterator
  : public iterator<input_iterator_tag, charT,
                    typename traits::off_type, charT* unspecified, charT> {

Change 27.6.3 [istreambuf.iterator], p1:

The class template istreambuf_iterator reads successive characters from the streambuf for which it was constructed. operator* provides access to the current input character, if any. [Note: operator-> may return a proxy. — end note] Each time operator++ is evaluated, the iterator advances to the next input character. If the end of stream is reached (streambuf_type::sgetc() returns traits::eof()), the iterator becomes equal to the end of stream iterator value. The default constructor istreambuf_iterator() and the constructor istreambuf_iterator(0) both construct an end of stream iterator object suitable for use as an end-of-range.