Topic: istream_iterator<T> and T's default constructor


Author: Dave Abrahams <abrahams@mediaone.net>
Date: 2000/04/26
Raw View
in article hinnant-2204001455430001@ith3-4b6.twcny.rr.com, Howard Hinnant at
hinnant@anti-spam_metrowerks.com wrote on 4/23/00 6:32 AM:

> I tried to post this yesterday, but it evidently got lost somewhere:
>
> One possibility is to create a knock off of istream_iterator, say
> constructor_iterator that (as Dave suggests), requires that Widget has a
> (istream&) constructor.  Might look something like (untested!):
>
> template <class T, class charT, class traits, class Distance>
> inline
> constructor_iterator<T, charT, traits, Distance>&
> constructor_iterator<T, charT, traits, Distance>::operator++()
> {
>     value.~T();
>     new (&value) T(stream);  // add eof checking

Sorry, but this in particular is a very bad idea.

It is a common temptation to explicitly destroy an object whose lifetime is
not really ending, but you must resist ;)

The problem occurs when an exception is thrown before the object can be
completely reconstructed. In this case, value is a data member of the
constructor_iterator and will be mistakenly destroyed again when the
constructor_iterator is destroyed.

This constructor_iterator adds little or nothing of use that I can see. It
would seem much simpler, if you were going to require a constructor
accepting istream& parameters, to just use the constructor directly:

int widget_count;
s >> widget_count;
std::vector<Widget> v;
for (int i = 0; i < widget_count; ++i)
   v.push_back(Widget(s));

-or-

std::vector<boost::shared_ptr<Widget> > v;
for (int i = 0; i < widget_count; ++i)
   v.push_back(boost::shared_ptr<Widget>( new Widget(s) ));


just-say-no-ly y'rs,
dave

---
[ 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: hinnant@anti-spam_metrowerks.com (Howard Hinnant)
Date: 2000/04/27
Raw View
In article <B52792C2.4E3A%abrahams@mediaone.net>, Dave Abrahams
<abrahams@mediaone.net> wrote:

> in article hinnant-2204001455430001@ith3-4b6.twcny.rr.com, Howard Hinnant at
> hinnant@anti-spam_metrowerks.com wrote on 4/23/00 6:32 AM:
>
> > template <class T, class charT, class traits, class Distance>
> > inline
> > constructor_iterator<T, charT, traits, Distance>&
> > constructor_iterator<T, charT, traits, Distance>::operator++()
> > {
> >     value.~T();
> >     new (&value) T(stream);  // add eof checking
>
> Sorry, but this in particular is a very bad idea.
>
> It is a common temptation to explicitly destroy an object whose lifetime is
> not really ending, but you must resist ;)
>
> The problem occurs when an exception is thrown before the object can be
> completely reconstructed. In this case, value is a data member of the
> constructor_iterator and will be mistakenly destroyed again when the
> constructor_iterator is destroyed.

True enough.  My proposal was meant as something to jump start an idea,
not a finished product.  Proper exception handling behavior should
certainly be part of the finished product, and was not considered during
this "back of the envenlope" scratching.  Probably should have been.

If one were interested in such an iterator, I still see possibilities on
how to create one and have it have acceptable exception handling
characteristics using bascially what I've outlined above.  But I haven't
actually created one so I'm sure that there are more details I'm missing.

One might want such an interator instead of:

> std::vector<boost::shared_ptr<Widget> > v;
> for (int i = 0; i < widget_count; ++i)
>    v.push_back(boost::shared_ptr<Widget>( new Widget(s) ));

if you wanted to use an existing algorithm in an "out of core" manner.
For example:

std::ifstream infile("in");
std::ofstream outfile("out");
constructor_iterator<T> in(infile), eof;
std::ostream_iterator<T> out(outfile, " ");
std::unique_copy(in, eof, out);

-Howard

---
[ 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: kanze@gabi-soft.de
Date: 2000/05/02
Raw View
Dave Abrahams <abrahams@mediaone.net> writes:

|>  in article MPG.136aeb48421ce259896e4@news.supernews.com, Scott Meyers=
 at
|>  smeyers@aristeia.com wrote on 4/24/00 1:22 AM:

|>  If we instead ignore operator>>(), we could produce an
|>  istream_iterator<T> that "does the right thing" by returning a T it
|>  constructs from the stream when dereferenced.

Which raises the argument: what does it require as a constructor?
(Perhaps a MyClass( istream& )?)

--=20
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient=E9e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh=FCttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627

---
[ 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: smeyers@aristeia.com (Scott Meyers)
Date: 2000/04/22
Raw View
Well, I just got a rude awakening.  I was trying to read a stream of
objects of my type Widget using istream_iterators, and my compilers all
complained about the lack of a default constructor.  I didn't see the need
for one, because I'd defined a valid operator>> for Widgets, but with all
my compilers agreeing (not a terribly common occurrence), I figured I had
to be wrong.  I consulted Langer/Kreft and then the standard, and both show
an istream_iterator<T> holding an object of type T, but both give the
comment "Exposition only".  Both also define the semantics of operator* to
return the internally stored value.  (I personally find this mildly
amusing.  How can the official semantics of a function be to return a
variable that exists for exposition only?)

Anyway, it seems clear to me that istream_iterators will work only with
types offering a default constructor, but I have two questions.

  - Was this a deliberate design decision on the part of the
    standardization committee, or did it just sort of work out this way?

  - Is the restriction explicitly mentioned somewhere in the standard?  I
    didn't peruse all the iterator requirements, etc.

Thanks,

Scott

--
"Effective STL" Seminar     June 7-9     Portland, Oregon
Details at http://www.trekservices.com/estl/

---
[ 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: hinnant@anti-spam_metrowerks.com (Howard Hinnant)
Date: 2000/04/22
Raw View
In article <MPG.13699c291c80bf1e9896e2@news.supernews.com>,
smeyers@aristeia.com (Scott Meyers) wrote:

> Well, I just got a rude awakening.  I was trying to read a stream of
> objects of my type Widget using istream_iterators, and my compilers all
> complained about the lack of a default constructor.  I didn't see the need
> for one, because I'd defined a valid operator>> for Widgets, but with all
> my compilers agreeing (not a terribly common occurrence), I figured I had
> to be wrong.  I consulted Langer/Kreft and then the standard, and both show
> an istream_iterator<T> holding an object of type T, but both give the
> comment "Exposition only".  Both also define the semantics of operator* to
> return the internally stored value.  (I personally find this mildly
> amusing.  How can the official semantics of a function be to return a
> variable that exists for exposition only?)
>
> Anyway, it seems clear to me that istream_iterators will work only with
> types offering a default constructor, but I have two questions.
>
>   - Was this a deliberate design decision on the part of the
>     standardization committee, or did it just sort of work out this way?
>
>   - Is the restriction explicitly mentioned somewhere in the standard?  I
>     didn't peruse all the iterator requirements, etc.

I hate it when people answer a question with another question, but here I
go anyway... ;-)

Since istream_iterator is known to read using operator>>, can you
construct a reasonable scenario of reading in a type using operator>>
without default constructing that type first?

T x;
stream >> x;

One could perhaps use copy construction and/or some non-default constructor:

T x(T(3));
stream >> x;

And now one could extend this idea to istream_iterator with some kind of
fancier constructor:

   istream_iterator(const T& default = T());
   istream_iterator(istream_type& s, const T& default = T());

So now the istream_iterator could have a non-default constructed T to
construct its internal value with before it reads into it.  But is this
worth it?  It's kind of hard to explain the purpose of default until
you've been through the kind of pain that you've just been through.  The
beginner asks:  Why the heck does the iterator want a default value that
it is never going to use?!

Maybe it is worth it.  As a customer of the standard, you tell me.

-Howard

PS:  or maybe there's a better way to accomplish this?

---
[ 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: Chris Riesbeck <riesbeck@ils.nwu.edu>
Date: 2000/04/22
Raw View
In article <MPG.13699c291c80bf1e9896e2@news.supernews.com>,
smeyers@aristeia.com (Scott Meyers) wrote:

>Anyway, it seems clear to me that istream_iterators will work only with
>types offering a default constructor, but I have two questions.
>
>  - Was this a deliberate design decision on the part of the
>    standardization committee, or did it just sort of work out this way?
>
>  - Is the restriction explicitly mentioned somewhere in the standard?  I
>    didn't peruse all the iterator requirements, etc.

Austern's Generic Programming and the STL says that the value
type T in istream_iterator must be a model of Default Constructible
(p 355). Not the standard, but strongly suggests it's there
somewhere.

---
[ 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: Dave Abrahams <abrahams@mediaone.net>
Date: 2000/04/23
Raw View
in article hinnant-2104001252190001@ith3-4ef.twcny.rr.com, Howard Hinnant at
hinnant@anti-spam_metrowerks.com wrote on 4/22/00 4:08 AM:

> I hate it when people answer a question with another question, but here I
> go anyway... ;-)
>
> Since istream_iterator is known to read using operator>>, can you
> construct a reasonable scenario of reading in a type using operator>>
> without default constructing that type first?
>

The "obvious" alternative is to require T to have a constructor which takes
an istream& parameter. So, operator>>(istream&, T& target) constructs a T
from the istream and copy-assigns it into the target value.

Oops! Where did the target value come from? Someone had to construct one
somehow. No default constructor? Too bad; you'll need to find some way to
construct one. What a waste - it's going to be rebuilt when it is
copy-assigned.

For most input streaming operations I would just use a Widget(istream&)
constructor directly. Much more direct, eh? Also, it doesn't require Widget
to be Assignable.

-Dave

---
[ 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: hinnant@anti-spam_metrowerks.com (Howard Hinnant)
Date: 2000/04/23
Raw View
In article <B52733B7.4D7A%abrahams@mediaone.net>, Dave Abrahams
<abrahams@mediaone.net> wrote:

> in article hinnant-2104001252190001@ith3-4ef.twcny.rr.com, Howard Hinnant at
> hinnant@anti-spam_metrowerks.com wrote on 4/22/00 4:08 AM:
>
> > I hate it when people answer a question with another question, but here I
> > go anyway... ;-)
> >
> > Since istream_iterator is known to read using operator>>, can you
> > construct a reasonable scenario of reading in a type using operator>>
> > without default constructing that type first?
> >
>
> The "obvious" alternative is to require T to have a constructor which takes
> an istream& parameter. So, operator>>(istream&, T& target) constructs a T
> from the istream and copy-assigns it into the target value.
>
> Oops! Where did the target value come from? Someone had to construct one
> somehow. No default constructor? Too bad; you'll need to find some way to
> construct one. What a waste - it's going to be rebuilt when it is
> copy-assigned.
>
> For most input streaming operations I would just use a Widget(istream&)
> constructor directly. Much more direct, eh? Also, it doesn't require Widget
> to be Assignable.

I tried to post this yesterday, but it evidently got lost somewhere:

One possibility is to create a knock off of istream_iterator, say
constructor_iterator that (as Dave suggests), requires that Widget has a
(istream&) constructor.  Might look something like (untested!):

template <class T, class charT, class traits, class Distance>
inline
constructor_iterator<T, charT, traits, Distance>&
constructor_iterator<T, charT, traits, Distance>::operator++()
{
   value.~T();
   new (&value) T(stream);  // add eof checking
     // add eof checking here.
   return *this;
}

value could get constructed the same way during constructor_iterator's
constructor.

template <class T, class charT, class traits, class Distance>
inline
constructor_iterator<T, charT, traits,
Distance>::constructor_iterator(istream_type& s)
   : stream(&s),
     value(s)
{
     // add eof checking here.
}

And you might evolve a class from this that specified the read/construct
policy with template parameters or a traits class.

-Howard

---
[ 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: smeyers@aristeia.com (Scott Meyers)
Date: 2000/04/24
Raw View
> Maybe it is worth it.  As a customer of the standard, you tell me.

I don't think it's worth it.  This is just another place where the illusion
of uniformity in the STL shows itself to be, well, an illusion.  Streams
*aren't* really containers (in the colloquial sense of the term), and
istream_iterators can't hide that as well as I'd expected them to be able
to.  It's too bad, because this puts more pressure on class designers to
add gratuitous default constructors to their classes.

Scott

--
"Effective STL" Seminar     June 7-9     Portland, Oregon
Details at http://www.trekservices.com/estl/

---
[ 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: hinnant@anti-spam_metrowerks.com (Howard Hinnant)
Date: 2000/04/24
Raw View
I should have added this to my previous post, but I hit the send key
before I thought about it.

How 'bout just creating a new class:   constructor_iterator that requires
different read semantics, but not default construction.  The innards might
look something like:

template <class T, class charT, class traits, class Distance>
inline
constructor_iterator<T, charT, traits, Distance>&
constructor_iterator<T, charT, traits, Distance>::operator++()
{
   value.~T();
   new (&value) T(stream);
   return *this;
}

Requires T to have a constructor that takes an istream.

And maybe from this could evolve into an input_iterator class that took
its read/construction policy from a template parameter or traits class...

-Howard

---
[ 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: Dave Abrahams <abrahams@mediaone.net>
Date: 2000/04/24
Raw View
in article MPG.136aeb48421ce259896e4@news.supernews.com, Scott Meyers at
smeyers@aristeia.com wrote on 4/24/00 1:22 AM:

>> Maybe it is worth it.  As a customer of the standard, you tell me.
>
> I don't think it's worth it.  This is just another place where the illusion
> of uniformity in the STL shows itself to be, well, an illusion. Streams
> *aren't* really containers (in the colloquial sense of the term), and
> istream_iterators can't hide that as well as I'd expected them to be able
> to.

Hmm. I always thought that istream_iterators were a cute hack but probably
not particularly useful, which I suppose is why I never had that reaction.

The reason for the breakdown of the illusion that a stream is a sequence
really doesn't have so much to do with the STL, but with the attempt to
integrate it with earlier idioms (i.e. streaming via operator>>()).

> It's too bad, because this puts more pressure on class designers to
> add gratuitous default constructors to their classes.

I don't know about this. If you buy the premise that objects should be
initialized at their point of first use (and I think you do), it quickly
becomes clear that operator>>() is wrong for most input streaming.

Ignoring the STL altogether, input streaming via operator>>() tends to
require default constructors (or some other equally gratuitous constructor
formulation).

If we instead ignore operator>>(), we could produce an istream_iterator<T>
that "does the right thing" by returning a T it constructs from the stream
when dereferenced.

-Dave

---
[ 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: Dave Abrahams <abrahams@mediaone.net>
Date: 2000/04/26
Raw View
in article hinnant-2204001455430001@ith3-4b6.twcny.rr.com, Howard Hinnant at
hinnant@anti-spam_metrowerks.com wrote on 4/23/00 6:32 AM:

> I tried to post this yesterday, but it evidently got lost somewhere:
>
> One possibility is to create a knock off of istream_iterator, say
> constructor_iterator that (as Dave suggests), requires that Widget has a
> (istream&) constructor.  Might look something like (untested!):
>
> template <class T, class charT, class traits, class Distance>
> inline
> constructor_iterator<T, charT, traits, Distance>&
> constructor_iterator<T, charT, traits, Distance>::operator++()
> {
>     value.~T();
>     new (&value) T(stream);  // add eof checking

Sorry, but this in particular is a very bad idea.

It is a common temptation to explicitly destroy an object whose lifetime is
not really ending, but you must resist ;)

The problem occurs when an exception is thrown before the object can be
completely reconstructed. In this case, value is a data member of the
constructor_iterator and will be mistakenly destroyed again when the
constructor_iterator is destroyed.

This constructor_iterator adds little or nothing of use that I can see. It
would seem much simpler, if you were going to require a constructor
accepting istream& parameters, to just use the constructor directly:

int widget_count;
s >> widget_count;
std::vector<Widget> v;
for (int i = 0; i < widget_count; ++i)
   v.push_back(Widget(s));

-or-

std::vector<boost::shared_ptr<Widget> > v;
for (int i = 0; i < widget_count; ++i)
   v.push_back(boost::shared_ptr<Widget>( new Widget(s) ));


just-say-no-ly y'rs,
dave

---
[ 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              ]