istreambuf_iterator
end-of-stream equalitySection: 24.6.4 [istreambuf.iterator] Status: New Submitter: Hyman Rosen Opened: 2014-02-19 Last modified: 2023-04-13
Priority: 3
View other active issues in [istreambuf.iterator].
View all other issues in [istreambuf.iterator].
View all issues with New status.
Discussion:
Given the following code,
#include <sstream> std::stringbuf buf; std::istreambuf_iterator<char> begin(&buf); std::istreambuf_iterator<char> end;
it is not clear from the wording of the Standard whether begin.equal(end)
must be true. In at least one implementation it is not (CC: Sun C++ 5.10 SunOS_sparc Patch 128228-25 2013/02/20) and in at least
one implementation it is (gcc version 4.3.2 x86_64-unknown-linux-gnu).
end
is an end-of-stream iterator since it was default
constructed. It also says that an iterator becomes equal to an end-of-stream
iterator when end of stream is reached by sgetc()
having returned eof()
.
[istreambuf.iterator::equal] says that equal()
returns true iff both iterators are end of stream
or not end of stream. But there seems to be no requirement that equal
check for end-of-stream by calling sgetc()
.
Jiahan Zi at BloombergLP discovered this issue through his code failing to
work correctly. Dietmar Kühl has opined in a private communication that
the iterators should compare equal.
[2023-03-31; Jonathan Wakely comments]
I agree that they should compare equal, but that's in conflict with the
resolution of LWG 2544, which says that begin
must not be at end-of-stream because &buf
is not null.
[2023-04-12; Jonathan adds wording]
Proposed resolution:
This wording is relative to N4944.
Change 24.6.4.1 [istreambuf.iterator.general] as indicated:
constexpr istreambuf_iterator() noexcept; constexpr istreambuf_iterator(default_sentinel_t) noexcept; istreambuf_iterator(const istreambuf_iterator&) noexcept = default; ~istreambuf_iterator() = default; istreambuf_iterator(istream_type& s) noexcept
;: istreambuf_iterator(s.rdbuf()) { } istreambuf_iterator(streambuf_type* s) noexcept; istreambuf_iterator(const proxy& p) noexcept; … private: streambuf_type* sbuf_; // exposition only int_type c_{}; // exposition only }; }
Change 24.6.4.3 [istreambuf.iterator.cons] as indicated:
For each
istreambuf_iterator
constructor in this section, an end-of-stream iterator is constructed ifand only ifthe exposition-only membersbuf_
is initialized with a null pointer value or ifsbuf_->sgetc()
returnstraits_type::eof()
.constexpr istreambuf_iterator() noexcept; constexpr istreambuf_iterator(default_sentinel_t) noexcept;-1- Effects: Initializes
sbuf_
withnullptr
.istreambuf_iterator(istream_type& s) noexcept;
-2- Effects: Initializessbuf_
withs.rdbuf()
.istreambuf_iterator(streambuf_type* s) noexcept;[Drafting note:
sgetc()
can throw, but this function isnoexcept
. Should it swallow exceptions and create an end-of-stream iterator, to avoid weakening the exception spec of an existing function?]-3- Effects: Initializes
sbuf_
withs
. Ifs
is not null, initializesc_
withs->sgetc()
. Setssbuf_
to null ifsgetc
exits via an exception, or iftraits_type::eq_int_type(c_, traits_type::eof())
istrue
.istreambuf_iterator(const proxy& p) noexcept;-4- Effects: Initializes
sbuf_
withp.sbuf_
. Ifp.sbuf_
is not null, initializesc_
withp.keep_
.
Change 24.6.4.4 [istreambuf.iterator.ops] as indicated:
charT operator*() const;-?- Preconditions:
sbuf_
is not null.-1- Returns:
The character obtained via thestreambuf
membersbuf_->sgetc()
.traits_type::to_char_type(c_)
.-?- Throws: Nothing.
istreambuf_iterator& operator++();-?- Preconditions:
sbuf_
is not null.-2- Effects:
As if byPerformssbuf_->sbumpc()
.c_ = sbuf_->snextc()
, then setssbuf_
to null iftraits_type::eq_int_type(c_, traits_type::eof())
istrue
.-3- Returns:
*this
.proxy operator++(int);-4-
Returns:proxy(sbuf_->sbumpc(), sbuf_)
.
Effects: Equivalent to:proxy p(**this, sbuf_); ++*this; return p;
bool equal(const istreambuf_iterator& b) const;-5- Returns:
bool(sbuf_) == bool(b.sbuf_)
.[Note: This is
true
if and only if both iterators are at end-of-stream, or neither is at end-of-stream, regardless of what streambuf object they use. — end note]template<class charT, class traits> bool operator==(const istreambuf_iterator<charT, traits>& a, const istreambuf_iterator<charT, traits>& b);-6- Returns:
a.equal(b)
.bool equal(const istreambuf_iterator& i, default_sentinel_t s) const;-7- Returns:
.
i.equal(s)i.sbuf_ == nullptr