Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2005-02-15 09:49:59


Don G <dongryphon_at_[hidden]> writes:

> Hi Peter,
>
> Again, thanks for taking all the time to patiently reply. This will
> in all likelihood be my final post on this topic, though I will read
> thoroughly any replies from you or others.
>
>>> I guess this is the crux of the my contention. I think all must
>>> agree there must be: automatic counting; basic pointer syntax
>>> (including any implicit conversion from derived to base, and
>>> various operators). The features beyond those are of the
>>> debatable variety.
>>
>> There are two features beyond what you listed:
>>
>> 1. "Do the right thing" destruction;
>> 2. Weak pointer support.
>
> I meant that I view these as debatable, not "absolutely essential".
>
>> Note that (1) does not equal "custom deleter support". Custom
>> deleters do not add any further overhead (when not used) over "do
>> the right thing" destruction.
>>
>> By that I mean that shared_ptr<X> can always be destroyed when it
>> has been constructed properly, no matter whether at the point of
>> destruction X is a complete type with an accessible (and virtual,
>> if the actual object is not of type X) destructor
>
> But if I have been using T* all these years, "do the right thing" is
> really just adding overhead for no benefit to me. I have to rethink
> all my options in light of this feature, because it is not how C++
> pointers behave. Some may argue "better"; others will argue
> "bloated".

I know Bjarne is very concerned about what happens to software when
programmers get the smart pointer religion. He claims to have seen
programs that got huge and slow when people stopped using raw
pointers.

>> or whether 'operator delete' at the point of destruction can
>> deallocate from the 'operator new' heap at the point of
> construction.
>
> Quite an esoteric condition.

Not at all; it's a very common concern for Windows programmers.

> I cannot imagine that shared_ptr would fit such an ABI even given
> this ability. In my experience, when this kind of isolation is in
> play, self destruction (via vtable as in COM) or similar techniques
> are also used.
>
>> The theoretical minimum cost of a non-intrusive pointer is:
>>
>> - per-instance pointer to the count;
>> - one word for the count.
>
> I agree, but would say it this way: non-intrusion costs sizeof
> pointer
> for the pointer to the count. The count itself doesn't seem fair to
> even call overhead<g> - I mean, you asked for this part by entering
> the world of reference counting! :)
>
>> Feature (1) above adds:
>>
>> - vtable
>> - pointer
>>
>> The functor need not be present when not used, although the current
>> code base does not do that. It can also be optimized out when it's
>> an empty class with the help of boost::compressed_pair. Again, the
>> current code base doesn't do it, but we're talking about the
>> design-imposed overhead here.
>
> I will look at compressed_pair, but I don't see how the functor could
> not exist or take no space.

It's known as the "Empty Base Optimization" (EBO).

>> One situation where custom deleters come in handy is when you want
>> to use your own smart pointer in an application, but a third-party
>> library takes a shared_ptr. With a shared_ptr not supporting
>> deleters, you'd be forced to use shared_ptr in your application,
>> too.
>
> Welcome to the happy world of 3rd party libraries :) Seriously, it is
> very common to encounter this type of thing, and it again comes back
> to the problem that custom deleters add most (all?) of their weight
> even if you don't use them.

What weight? They only really need to cost a single function pointer.

>> All designs require a certain irreducible overhead, do they not?
>> Think of it as a std::map. The overhead of a map is not clearly
>> specified or articulated. (B) probably applies as well. But it
>> works.
>
> Absolutely, all designs have certain irreducible overhead - I didn't
> mean to imply otherwise. In the case of std::map, one should expect
> an instance to be on the large-ish side. Also, one should expect the
> per node overhead to be two pointers and probably another word for
> the tree balancing algorithm.

You don't need any storage for rebalancing. Typical map nodes have 3
pointers: parent, left child, and right child.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk