Decision on container design
Jonathan M Davis
jmdavisProg at gmx.com
Tue Feb 1 09:15:30 PST 2011
On Tuesday 01 February 2011 09:07:55 Andrei Alexandrescu wrote:
> On 2/1/11 10:44 AM, Michel Fortin wrote:
> > On 2011-02-01 11:12:13 -0500, Andrei Alexandrescu
> >
> > <SeeWebsiteForEmail at erdani.org> said:
> >> On 1/28/11 8:12 PM, Michel Fortin wrote:
> >>> On 2011-01-28 20:10:06 -0500, "Denis Koroskin" <2korden at gmail.com> said:
> >>>> Unfortunately, this design has big issues:
> >>>>
> >>>>
> >>>> void fill(Appender appender)
> >>>> {
> >>>> appender.put("hello");
> >>>> appender.put("world");
> >>>> }
> >>>>
> >>>> void test()
> >>>> {
> >>>> Appender<string> appender;
> >>>> fill(appender); // Appender is supposed to have reference semantics
> >>>> assert(appender.length != 0); // fails!
> >>>> }
> >>>>
> >>>> Asserting above fails because at the time you pass appender object to
> >>>> the fill method it isn't initialized yet (lazy initialization). As
> >>>> such, a null is passed, creating an instance at first appending, but
> >>>> the result isn't seen to the caller.
> >>>
> >>> That's indeed a problem. I don't think it's a fatal flaw however, given
> >>> that the idiom already exists in AAs.
> >>>
> >>> That said, the nice thing about my proposal is that you can easily
> >>> reuse the Impl to create a new container to build a new container
> >>> wrapper with the semantics you like with no loss of efficiency.
> >>>
> >>> As for the case of Appender... personally in the case above I'd be
> >>> tempted to use Appender.Impl directly (value semantics) and make fill
> >>> take a 'ref'. There's no point in having an extra heap allocation,
> >>> especially if you're calling test() in a loop or if there's a good
> >>> chance fill() has nothing to append to it.
> >>>
> >>> That's the issue with containers. The optimal semantics always change
> >>> depending on the use case.
> >>
> >> Yep, yep, I found myself wrestling with the same issues. All good
> >> points. On one hand containers are a target for optimization because
> >> many will use them. On the other hand you'd want to have reasonably
> >> simple and idiomatic code in the container implementation because you
> >> want people to understand them easily and also to write their own. I
> >> thought for a while of a layered approach in which you'd have both the
> >> value and the sealed reference version of a container... it's just too
> >> much aggravation.
> >
> > But are you not just pushing the aggravation elsewhere? If I need a by
> > value container for some reason (performance or semantics) I'll have to
> > write my own, and likely others will write their own too.
>
> If semantics are the primary concern, you could (and in fact Phobos
> could) provide a Value!C template that automatically calls dup in
> this(this) etc.
>
> For performance I agree there is stuff that class containers leave on
> the table.
>
> > Using classes for containers is just marginally better than making them
> > by-value structs: you can use 'new' with a by-value struct if you want
> > it to behave as a class-like by-reference container:
> >
> > struct Container {
> > ...
> > }
> >
> > auto c = new Container();
> >
> > The only noticeable difference from a class container is that now c is
> > now a Container*.
>
> Well one problem now is that if you have a Container* you don't know
> whether it's dynamically allocated or the address of some
> stack-allocated object. This is pretty big; a major issue that I believe
> C++ has is that you can seldom reason modularly about functions because
> C++ makes it impossible to represent reference semantics with
> local/remote/shared/no ownership without resorting to convention.
>
> A better solution is to define something like
>
> auto c = new Classify!Container;
>
> which transforms a value into a class object.
>
> With this, the question becomes a matter of choosing the right default:
> do we want values most of the time and occasional references, or vice
> versa? I think most of the time you need references, as witnessed by the
> many '&'s out there in code working on STL containers.
Java implements containers as classes (not that it really has any other choice),
so all containers in Java have reference semantics, and I've _never_ found that
to be a problem. I do think that there are rare cases where it makes sense for a
container to be a value type, but I really do think that it's a rare case for
the average programmer. On the other hand, having containers be value types in
C++ is _frequently_ a problem.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list