Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

shared_pointer, enable_shared_from_this, and why doesn't it check things?

94 views
Skip to first unread message

Vir Campestris

unread,
Oct 13, 2017, 4:37:40 PM10/13/17
to
If you derive from enable_shared_from_this you get a method in your
class "shared_from_this" that allows you to hand out shared pointers safely.

The first time you assign a pointer from your object it sets up the
shared_ptr, links the internal weak_ptr, and all is fine.

If some **** later calls std::shared_ptr(this) you get an entirely new
shared pointer, with a different ref count.

If the STL is smart enough to detect the first case why doesn't it give
an error in the second?

I know a codebase where it would have saved lots of problems :(

Andy

Paavo Helde

unread,
Oct 13, 2017, 5:43:09 PM10/13/17
to
On 13.10.2017 23:37, Vir Campestris wrote:
> If you derive from enable_shared_from_this you get a method in your
> class "shared_from_this" that allows you to hand out shared pointers
> safely.
>
> The first time you assign a pointer from your object it sets up the
> shared_ptr, links the internal weak_ptr, and all is fine.
>
> If some **** later calls std::shared_ptr(this) you get an entirely new
> shared pointer, with a different ref count.
>
> If the STL is smart enough to detect the first case why doesn't it give
> an error in the second?

What first case? Do you mean that C++ lets you wrap any raw pointer in a
shared_ptr, regardless of if this makes sense or not? Yes, you are
welcome to shoot your both legs off!

Actually that's indeed a problem. I dealt with that by not using
std::shared_ptr directly, but deriving my own smartpointer class from it
and prohibiting the constructor and assignment from the raw pointer. The
only constructor is from std::shared_ptr (which is not used otherwise in
the codebase).

Allowing reconstruction the smartpointer from *this is another,
orthogonal topic. For that you need to derive from
std::enable_shared_from_this and call shared_from_this() (for which I
have provided my own function of course as well to wrap the result
immediately into my own smartpointer class).

If there are any better/cleaner solutions I would appreciate to hear
about them of course!

Cheers
Paavo

Alf P. Steinbach

unread,
Oct 13, 2017, 6:41:04 PM10/13/17
to
On 10/13/2017 11:42 PM, Paavo Helde wrote:
> On 13.10.2017 23:37, Vir Campestris wrote:
>> If you derive from enable_shared_from_this you get a method in your
>> class "shared_from_this" that allows you to hand out shared pointers
>> safely.
>>
>> The first time you assign a pointer from your object it sets up the
>> shared_ptr, links the internal weak_ptr, and all is fine.
>>
>> If some **** later calls std::shared_ptr(this) you get an entirely new
>> shared pointer, with a different ref count.
>>
>> If the STL is smart enough to detect the first case why doesn't it give
>> an error in the second?
>
> What first case? Do you mean that C++ lets you wrap any raw pointer in a
> shared_ptr, regardless of if this makes sense or not? Yes, you are
> welcome to shoot your both legs off!
>
> Actually that's indeed a problem. I dealt with that by not using
> std::shared_ptr directly, but deriving my own smartpointer class from it
> and prohibiting the constructor and assignment from the raw pointer. The
> only constructor is from std::shared_ptr (which is not used otherwise in
> the codebase).

I think maybe better to provide a constructor that forwards arguments to
make_shared.


> Allowing reconstruction the smartpointer from *this is another,
> orthogonal topic. For that you need to derive from
> std::enable_shared_from_this and call shared_from_this() (for which I
> have provided my own function of course as well to wrap the result
> immediately into my own smartpointer class).
>
> If there are any better/cleaner solutions I would appreciate to hear
> about them of course!

Cheers!,

- Alf

Paavo Helde

unread,
Oct 14, 2017, 3:23:18 AM10/14/17
to
On 14.10.2017 1:40, Alf P. Steinbach wrote:
> On 10/13/2017 11:42 PM, Paavo Helde wrote:
>> On 13.10.2017 23:37, Vir Campestris wrote:
>>> If you derive from enable_shared_from_this you get a method in your
>>> class "shared_from_this" that allows you to hand out shared pointers
>>> safely.
>>>
>>> The first time you assign a pointer from your object it sets up the
>>> shared_ptr, links the internal weak_ptr, and all is fine.
>>>
>>> If some **** later calls std::shared_ptr(this) you get an entirely new
>>> shared pointer, with a different ref count.
>>>
>>> If the STL is smart enough to detect the first case why doesn't it give
>>> an error in the second?
>>
>> What first case? Do you mean that C++ lets you wrap any raw pointer in
>> a shared_ptr, regardless of if this makes sense or not? Yes, you are
>> welcome to shoot your both legs off!
>>
>> Actually that's indeed a problem. I dealt with that by not using
>> std::shared_ptr directly, but deriving my own smartpointer class from
>> it and prohibiting the constructor and assignment from the raw
>> pointer. The only constructor is from std::shared_ptr (which is not
>> used otherwise in the codebase).
>
> I think maybe better to provide a constructor that forwards arguments to
> make_shared.

You mean a static factory function? Yes I do that as well. But this does
not solve the issue that a new std::shared_ptr can be constructed
accidentally from 'this' or from a C++ reference to the object.

Such a construction would actually work for some kind of smartpointers,
but not for std::shared_ptr, and may create a lot of hidden bugs in the
codebase when replacing the old smartpointer by the standard
std::shared_ptr. Been there, done that...

Cheers
Paavo

Öö Tiib

unread,
Oct 14, 2017, 9:27:45 AM10/14/17
to
On Friday, 13 October 2017 23:37:40 UTC+3, Vir Campestris wrote:
> If you derive from enable_shared_from_this you get a method in your
> class "shared_from_this" that allows you to hand out shared pointers safely.
>
> The first time you assign a pointer from your object it sets up the
> shared_ptr, links the internal weak_ptr, and all is fine.

Yes you are correct. Note that it is unusual case since the programmers
usually create new shared_ptr with make_shared or allocate_shared or
provide also deleter as well.

When someone constructs shared_ptr from raw pointer (without
also providing deleter) then I feel that it strongly smells like
newbie and is potentially copy-paste from stackoverflow or other
newbie site. Can anyone bring sane counter-example?

> If some **** later calls std::shared_ptr(this) you get an entirely new
> shared pointer, with a different ref count.

Yes, when constructing a std::shared_ptr for an object that is
already managed by another std::shared_ptr then constructor is not
required to check for that (despite it is required to store weak
reference to enable_shared_from_this subobject) and so it will always
lead to undefined behavior.

> If the STL is smart enough to detect the first case why doesn't it give
> an error in the second?
>
> I know a codebase where it would have saved lots of problems :(

Because it is not required. The 'enable_shared_from_this' was limited
feature up to C++17 since there was even no way to check if the object
was managed by 'shared_ptr' at all or wasn't. Therefore usage of
'enable_shared_from_this' involved ensuring that *every* object
of that type was *always* already managed by 'shared_ptr'.

From C++17 we can finally make a check that (note how confusing
interface) when 'pT->weak_from_this().expired()' then the object
pointed at is not managed by 'shared_ptr<T>'. That still does
not make constructing shared_ptr from pT non-smelly or non-dangerous.
However it at least removes the curse that something derived from
'enable_shared_from_this' was not passable by value, usable as
automatic object, usable as element of container or usable as
member of class without rendering the whole 'enable_shared_from_this'
useless.

Alf P. Steinbach

unread,
Oct 14, 2017, 2:44:25 PM10/14/17
to
I don't get what you mean here.

I'm not familiar with the `expired` function but one could always check
a weak pointer by trying to convert it to `shared_ptr`, and, checking in
cppreference, I see that `expired` was introduced in C++11 (maybe it was
there always in Boost, I don't know, but since the function is is
irrelevant its date of introduction doesn't matter).

As far as I know C++17 still lacks a way to check if a type T is derived
from some accessible and unique base class
`enabled_shared_from_this<U>`, doesn't it?


> That still does
> not make constructing shared_ptr from pT non-smelly or non-dangerous.
> However it at least removes the curse that something derived from
> 'enable_shared_from_this' was not passable by value, usable as
> automatic object, usable as element of container or usable as
> member of class without rendering the whole 'enable_shared_from_this'
> useless.

I don't get this either.

I think there is an issue with /moving/ an object of a type derived from
`enable_shared_from_this`.

But a weak pointer can be copied just fine, can't it?


Cheers!,

- Alf

Alf P. Steinbach

unread,
Oct 14, 2017, 2:47:30 PM10/14/17
to
Not entirely sure what you mean, but I didn't mean in-addition-to, I
meant instead-of.

Sort of like this:


namespace cppx {
using std::enable_if_t;
using std::enable_shared_from_this;
using std::forward;
using std::make_shared;
using std::shared_ptr;

template< class D, class B >
using Is_derived_and_base = std::is_base_of<B, D>;

template< class Referent >
class Shared_ptr
: public shared_ptr<Referent>
{
public:
template< class... Args >
Shared_ptr( Args&&... args )
: shared_ptr<Referent>( make_shared<Referent>(
forward<Args>( args )... ) )
{}
};
} // namespace cppx



Possibly also allow construction from a raw pointer to type derived from
`enable_shared_from_this`, and possibly also support construction from
raw pointer + deleter, but not just from any raw pointer.



> Such a construction would actually work for some kind of smartpointers,
> but not for std::shared_ptr, and may create a lot of hidden bugs in the
> codebase when replacing the old smartpointer by the standard
> std::shared_ptr.

Uhm?


Cheers!,

- Alf

Öö Tiib

unread,
Oct 14, 2017, 3:35:56 PM10/14/17
to
I will gladly try to explain then.

> I'm not familiar with the `expired` function but one could always check
> a weak pointer by trying to convert it to `shared_ptr`, and, checking in
> cppreference, I see that `expired` was introduced in C++11 (maybe it was
> there always in Boost, I don't know, but since the function is is
> irrelevant its date of introduction doesn't matter).

The issue was not checking if 'weak_ptr' is pointing at valid object.
The issue was that 'enable_shared_from_this::weak_from_this()' was not
yet present in C++14 and so calling pT->weak_from_this() did not compile.
Calling pT->shared_from_this() however was undefined behavior on case
the object pointed at by pT was not managed by shared_ptr in C++14 (it
throws bad_weak_ptr in C++17).

> As far as I know C++17 still lacks a way to check if a type T is derived
> from some accessible and unique base class
> `enabled_shared_from_this<U>`, doesn't it?

May be that is not needed. On general case 'std::is_base_of' is plenty.
To avoid edge cases it is required from programmer that the
'enable_shared_from_this' base should be unique and accessible.

> > That still does
> > not make constructing shared_ptr from pT non-smelly or non-dangerous.
> > However it at least removes the curse that something derived from
> > 'enable_shared_from_this' was not passable by value, usable as
> > automatic object, usable as element of container or usable as
> > member of class without rendering the whole 'enable_shared_from_this'
> > useless.
>
> I don't get this either.

Here I am in dim what you don't get. I meant that now we can check if
object (derived publicly and uniquely from 'enable_shared_from_this')
is actually managed by shared_ptr. That significantly relaxes its
usage.

> I think there is an issue with /moving/ an object of a type derived from
> `enable_shared_from_this`.

Copying enable_shared_from_this does not copy the weak reference because
it does not make sense if to think about it a bit.

> But a weak pointer can be copied just fine, can't it?

Yes, copying weak_ptr increases weak count of managed object. With it
there are no issues.

Vir Campestris

unread,
Oct 14, 2017, 4:24:08 PM10/14/17
to
On 14/10/2017 14:27, Öö Tiib wrote:
> When someone constructs shared_ptr from raw pointer (without
> also providing deleter) then I feel that it strongly smells like
> newbie and is potentially copy-paste from stackoverflow or other
> newbie site. Can anyone bring sane counter-example?

Yes. If the constructor is private to force use of a factory function
make_shared won't compile. (I tried and failed to work out the correct
friend statement, then gave up)

Thanks everyone.

Andy

Öö Tiib

unread,
Oct 14, 2017, 8:44:38 PM10/14/17
to
What? Enforcing additional allocation and additional deallocation
just to force use of factory function? In general such schemes feel
like "I am schizophrenicly fighting with myself".

If you insist then it is possible to turn public constructor into not
callable by it having a pass-key parameter. That forces to use factory
function (that can produce such parameter). One extra parameter of
constructor is certainly cheaper than one extra alloction and
deallocation.

Paavo Helde

unread,
Oct 15, 2017, 3:45:21 AM10/15/17
to
On 15.10.2017 3:44, Öö Tiib wrote:
> On Saturday, 14 October 2017 23:24:08 UTC+3, Vir Campestris wrote:
>> On 14/10/2017 14:27, Öö Tiib wrote:
>>> When someone constructs shared_ptr from raw pointer (without
>>> also providing deleter) then I feel that it strongly smells like
>>> newbie and is potentially copy-paste from stackoverflow or other
>>> newbie site. Can anyone bring sane counter-example?
>>
>> Yes. If the constructor is private to force use of a factory function
>> make_shared won't compile. (I tried and failed to work out the correct
>> friend statement, then gave up)
>
> What? Enforcing additional allocation and additional deallocation
> just to force use of factory function? In general such schemes feel
> like "I am schizophrenicly fighting with myself".

This thread is about smartpointers. If the object is not dynamically
allocated, then there is no need for the smartpointer in the first place.

And yes, there are programs where all objects of some class need to be
dynamically allocated anyway. Allowing some casual automatic objects in
the mix would just complicate things like shared_from_this().

Cheers
Paavo

Öö Tiib

unread,
Oct 15, 2017, 5:23:01 AM10/15/17
to
On Sunday, 15 October 2017 10:45:21 UTC+3, Paavo Helde wrote:
> On 15.10.2017 3:44, Öö Tiib wrote:
> > On Saturday, 14 October 2017 23:24:08 UTC+3, Vir Campestris wrote:
> >> On 14/10/2017 14:27, Öö Tiib wrote:
> >>> When someone constructs shared_ptr from raw pointer (without
> >>> also providing deleter) then I feel that it strongly smells like
> >>> newbie and is potentially copy-paste from stackoverflow or other
> >>> newbie site. Can anyone bring sane counter-example?
> >>
> >> Yes. If the constructor is private to force use of a factory function
> >> make_shared won't compile. (I tried and failed to work out the correct
> >> friend statement, then gave up)
> >
> > What? Enforcing additional allocation and additional deallocation
> > just to force use of factory function? In general such schemes feel
> > like "I am schizophrenicly fighting with myself".
>
> This thread is about smartpointers. If the object is not dynamically
> allocated, then there is no need for the smartpointer in the first place.

Here is either something I was expressing badly or reading comprehension
issue. I have whole current thread discussed objects managed by smart
pointer, specifically by shared_ptr.

> And yes, there are programs where all objects of some class need to be
> dynamically allocated anyway. Allowing some casual automatic objects in
> the mix would just complicate things like shared_from_this().

That is mostly so but I had impression that we did not discuss it yet.
Seems that I expressed myself not clearly enough.

I was not talking about dynamic/automatic allocation of object with
"additional allocation". I was talking about class that was deliberately
made in a way where make_shared does not work (all the constructors
were made private). The excuse making class in that way was forcing
usage of factory function that was likely made for forcing usage of
shared_ptr.

Therefore separate dynamic allocation for shared state (that I meant
by "additional allocation") was made for each object within that
factory function by constructor of shared_ptr. How else? The
make_shared does not compile. That is insane example for me but YMMV.

Now coming back to what we haven't discussed yet. I agree with you
that typically we want objects of class to be managed in single
fashion to simplify things. There can also be a case where that
"single fashion" is shared_ptr. It makes perfect sense to have
factory function for it.

The whole enable_shared_from_this however is complicated performance
optimization for that case. Apparently author wants to pass around
references to object instead of shared_ptr as performance
optimization. Apparently author needs at the other end of such
reference-passing-chain still have the shared_ptr as complication.
Therefore complicated performance optimization.

Usage of that means like author is not entirely happy about performance.
Where programmer is unhappy about performance there flexibility may help
and there may be desire to relax that "single fashion". Note that I am
not saying that I know what helps just that it may. More efficient smart
pointer or some casual automatic object may help in tightest spot.
So it is only good if calling shared_from_this() of such objects is not
undefined behavior anymore in C++17.

Marcel Mueller

unread,
Oct 15, 2017, 8:12:34 AM10/15/17
to
If you have a code base that has problems with memory management, search
the code for /all/ occurrences of raw pointers, preferably the ones w/o
const, and remove them. Most code bases where I have done so never had
problems with undefined behavior again. Only NULL references remain, but
they are usually easy to fix.

If you do not want to go this far, switch to intrusive pointers. When
the ref count is part of the object, you can safely convert to a raw
pointer and back to an intrusive_ptr.
I usually add a base class to all objects that should be reference
countable. This class provides the ref count and it is the common
interface to the intrusive_ptr methods. If you missed one, the compiler
will tell you.
This method is also faster than shared_ptr.

I cannot understand why shared_ptr is recommended everywhere. In fact I
never wrote any production application that used shared_ptr. Instead I
usually avoid raw pointers (except for const char*) and additionally use
intrusive reference counts.


Marcel

Paavo Helde

unread,
Oct 15, 2017, 9:55:20 AM10/15/17
to
I see. I indeed misread your post.

> Therefore separate dynamic allocation for shared state (that I meant
> by "additional allocation") was made for each object within that
> factory function by constructor of shared_ptr. How else? The
> make_shared does not compile.

By making the constructor public of course so that make_shared starts to
compile. This is what I did in the same situation, so I assumed Vir did
the same (but maybe I'm mistaken).

Cheers
Paavo

Öö Tiib

unread,
Oct 15, 2017, 10:31:39 AM10/15/17
to
Vir brought that as "sane counter example" where make_shared can't be
used (and so constructing from raw pointer has to be used) if I
understood him correctly. For me making the make_shared impossible to
be called for forcing usage of shared_ptr is no way sane and so my
answer might be was too emotional.

I suggested to make it public and mentioned that there are ways
(if he really needs) to make public constructor tricky to call, but
that part you snipped.

Paavo Helde

unread,
Oct 15, 2017, 10:38:34 AM10/15/17
to
On 15.10.2017 15:12, Marcel Mueller wrote:
> On 13.10.17 22.37, Vir Campestris wrote:
>> If you derive from enable_shared_from_this you get a method in your
>> class "shared_from_this" that allows you to hand out shared pointers
>> safely.
>>
>> The first time you assign a pointer from your object it sets up the
>> shared_ptr, links the internal weak_ptr, and all is fine.
>>
>> If some **** later calls std::shared_ptr(this) you get an entirely new
>> shared pointer, with a different ref count.
>>
>> If the STL is smart enough to detect the first case why doesn't it give
>> an error in the second?
>>
>> I know a codebase where it would have saved lots of problems :(
>
> If you have a code base that has problems with memory management, search
> the code for /all/ occurrences of raw pointers, preferably the ones w/o
> const, and remove them. Most code bases where I have done so never had
> problems with undefined behavior again. Only NULL references remain, but
> they are usually easy to fix.
>
> If you do not want to go this far, switch to intrusive pointers. When
> the ref count is part of the object, you can safely convert to a raw
> pointer and back to an intrusive_ptr.

Only as long as you don't need weak pointers, and as long as you
carefully avoid doing this during construction and destruction.

> I usually add a base class to all objects that should be reference
> countable. This class provides the ref count and it is the common
> interface to the intrusive_ptr methods. If you missed one, the compiler
> will tell you.
> This method is also faster than shared_ptr.

Only if you don't use std::make_shared() or std::allocate_shared (like
you should).

> I cannot understand why shared_ptr is recommended everywhere. In fact I
> never wrote any production application that used shared_ptr. Instead I
> usually avoid raw pointers (except for const char*) and additionally use
> intrusive reference counts.

Because the people needing such recommendations are newbies, and newbies
tend to mess up their raw pointers and cannot be trusted to write their
own smartpointer classes either (especially if thread safety or weak
pointers are involved).

I agree there could be a lightweight performance-oriented smartpointer
class in the standard (intrusive refcount, no thread safety, no weak
pointers, etc), but regarding the recommendations this would not help
much because the newbies would not know if their code can use it or not.

Cheers
Paavo

Vir Campestris

unread,
Oct 15, 2017, 4:17:46 PM10/15/17
to
On 15/10/2017 13:12, Marcel Mueller wrote:
> If you have a code base that has problems with memory management, search
> the code for /all/ occurrences of raw pointers, preferably the ones w/o
> const, and remove them. Most code bases where I have done so never had
> problems with undefined behavior again. Only NULL references remain, but
> they are usually easy to fix.

I'm 6 weeks into a new job, and there are 782 occurrences of the string
"_ptr(this)".

I've fixed the one class where I could prove it was causing problems.
(now all are weak_ptr, shared_ptr, or reference) The rest will have to wait.

Andy

Vir Campestris

unread,
Oct 16, 2017, 4:30:28 PM10/16/17
to
On 15/10/2017 21:17, Vir Campestris wrote:
>
> I'm 6 weeks into a new job, and there are 782 occurrences of the string
> "_ptr(this)".
>
> I've fixed the one class where I could prove it was causing problems.
> (now all are weak_ptr, shared_ptr, or reference) The rest will have to
> wait.

I've now discovered it's a coding style problem (and I'm not the only
one to have been confused!)

There's a class right down there....>>> at the bottom of the hierarchy
with a member that reads something like this:

template <typename T> std::shared_ptr shared_ptr(T* foo)
{
return foo->shared_from_this();
}

Andy

Marcel Mueller

unread,
Oct 19, 2017, 2:00:44 PM10/19/17
to
On 15.10.17 16.38, Paavo Helde wrote:
>> If you do not want to go this far, switch to intrusive pointers. When
>> the ref count is part of the object, you can safely convert to a raw
>> pointer and back to an intrusive_ptr.
>
> Only as long as you don't need weak pointers, and as long as you

Indeed. Weak pointers are an indication for shared_ptr.

> carefully avoid doing this during construction and destruction.

?

I already had classes that managed their own lifetime with intrusive
reference counts.

What kind of problem do you have in mind?


[intrusive_ptr]
>> This method is also faster than shared_ptr.
>
> Only if you don't use std::make_shared() or std::allocate_shared (like
> you should).

The disadvantage is that you need to use the custom allocator all the way.


> I agree there could be a lightweight performance-oriented smartpointer
> class in the standard (intrusive refcount, no thread safety, no weak
> pointers, etc), but regarding the recommendations this would not help
> much because the newbies would not know if their code can use it or not.

For my own purposes I combined the efficiency and the thread-safety by
abusing the volatile quantifier. Only volatile instances invoke the
thread safe functions. They provide even strong thread safety.
Of course, this only applies to the access to the smart pointer itself.
The access to the reference counter need to use atomic
increment/decrement to allow the coexistence of thread-safe and local
instances that refer to the same object. That's the price.


Marcel

Paavo Helde

unread,
Oct 19, 2017, 6:01:39 PM10/19/17
to
On 19.10.2017 21:00, Marcel Mueller wrote:
> On 15.10.17 16.38, Paavo Helde wrote:
>>> If you do not want to go this far, switch to intrusive pointers. When
>>> the ref count is part of the object, you can safely convert to a raw
>>> pointer and back to an intrusive_ptr.
>>
>> Only as long as you don't need weak pointers, and as long as you
>
> Indeed. Weak pointers are an indication for shared_ptr.
>
>> carefully avoid doing this during construction and destruction.
>
> ?
>
> I already had classes that managed their own lifetime with intrusive
> reference counts.
>
> What kind of problem do you have in mind?

So what do you think will happen when you construct a refcounted
intrusive smartpointer from 'this' (or a raw pointer having the value of
'this') during the constructor or destructor call, maybe indirectly
through some other member functions?

In the constructor, if the extra smartpointer goes out of scope, your
object gets destroyed before it has even finished constructing. In
destructor, it would become destroyed twice.

This can be fixed and worked around (e.g. by avoiding constructing
smartpointers from 'this' during ctor/dtor), but then it would not be
such a simple and safe solution any more.

With std::shared_ptr an extra shared_ptr can be legally constructed only
from another shared_ptr which does not exist during ctor/dtor call;
calling shared_from_this() is not UB either since C++17 (throws an
exception which can be caught and handled).

>
>
> [intrusive_ptr]
>>> This method is also faster than shared_ptr.
>>
>> Only if you don't use std::make_shared() or std::allocate_shared (like
>> you should).
>
> The disadvantage is that you need to use the custom allocator all the way.

std::make_shared() uses the standard allocator.

cheers
paavo

Marcel Mueller

unread,
Oct 27, 2017, 11:21:08 AM10/27/17
to
On 20.10.17 00.01, Paavo Helde wrote:
>>> carefully avoid doing this during construction and destruction.
>>
>> ?
>>
>> I already had classes that managed their own lifetime with intrusive
>> reference counts.
>>
>> What kind of problem do you have in mind?
>
> So what do you think will happen when you construct a refcounted
> intrusive smartpointer from 'this' (or a raw pointer having the value of
> 'this') during the constructor or destructor call, maybe indirectly
> through some other member functions?
>
> In the constructor, if the extra smartpointer goes out of scope, your
> object gets destroyed before it has even finished constructing. In
> destructor, it would become destroyed twice.

OK, but classes with embedded lifetime management should be aware of
that and this is in no way unique to intrusive reference counts.

Typically their constructor is private an the public factory has
something like my_lifetime_management_container_with_smart_ptr.add(new
myclass()); which is perfectly valid.

In fact I have never seen a temporary smart pointer that is assigned
from this. I think this is always an anti-pattern. When I am already in
a instance method there is no need to deal with a smart pointer anymore.


>> [intrusive_ptr]
>>>> This method is also faster than shared_ptr.
>>>
>>> Only if you don't use std::make_shared() or std::allocate_shared (like
>>> you should).
>>
>> The disadvantage is that you need to use the custom allocator all the
>> way.
>
> std::make_shared() uses the standard allocator.

You are right. They provide the appropriate deleter.

But I still would prefer intrusive ref counts unless I need weak references.


Marcel

Richard

unread,
Oct 27, 2017, 12:11:48 PM10/27/17
to
[Please do not mail me a copy of your followup]

Marcel Mueller <news.5...@spamgourmet.org> spake the secret code
<osvisp$lvp$1...@gwaiyur.mb-net.net> thusly:

>But I still would prefer intrusive ref counts unless I need weak references.

I've worked in a code base with intrusive reference counts and in that
code base it meant that I couldn't forward declare such classes when I
wanted to hold them with an inline smart pointer as a member:

#inlucde <smart_ptr.h>

class A {
// ...
private:
smart_ptr<B> m_b;
};

Because smart_ptr's definition is inline, it exposes the inc/dec
operators on the enclosed class B that has an intrusive reference
count. Therefore I couldn't forward declare B, I had to include it's
complete declaration so that smart_ptr could see it's inc/dec
reference count methods.

It's not that I don't know how to solve this problem (include B's
header), it's that I'd rather just forward declare B instead.

Did you find a way to have such a smart pointer class for your
intrusive reference counted classes in such a way that you could do
type erasure and only forward declare B?
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Terminals Wiki <http://terminals-wiki.org>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Marcel Mueller

unread,
Oct 27, 2017, 12:45:09 PM10/27/17
to
On 27.10.17 18.11, Richard wrote:
> [Please do not mail me a copy of your followup]
>
> Marcel Mueller <news.5...@spamgourmet.org> spake the secret code
> <osvisp$lvp$1...@gwaiyur.mb-net.net> thusly:
>
>> But I still would prefer intrusive ref counts unless I need weak references.
>
> I've worked in a code base with intrusive reference counts and in that
> code base it meant that I couldn't forward declare such classes when I
> wanted to hold them with an inline smart pointer as a member:

Hmm, I am pretty sure that this worked for me in a larger project.
[...]
Just checked:
class PlayableInstance;
...
class Location : public Iref_count
{ ...
vector_int<PlayableInstance> Callstack;
...
}
vector_int<> was my implementation of a vector of intrusive smart
pointers. Obviously it could deal with the incomplete type.

I have also to admit that it was not std::intrusive_ptr, mainly because
C++11 was not existent that time.


> #inlucde <smart_ptr.h>
>
> class A {
> // ...
> private:
> smart_ptr<B> m_b;
> };
>
> Because smart_ptr's definition is inline, it exposes the inc/dec
> operators on the enclosed class B that has an intrusive reference
> count.

Hmm, wasn't there a rule that template functions are instantiated lazy?


> Did you find a way to have such a smart pointer class for your
> intrusive reference counted classes in such a way that you could do
> type erasure and only forward declare B?

In some way I must have managed this years ago.
Maybe the trick was that the destructor of Location wasn't inline. So
the destructor of the intrusive pointer was only instantiated in the
/implementation/ of Location, where all include files have been parsed
and the type PlayableInstance is complete. Unfortunately a quick sight
of the old source disproved the statement. The destructor was declared
in the class header though virtual.
No idea why this compiles. But I am sure that the type is really
incomplete during the definition of class Location because the header of
class PlayableInstance strongly depends on Location.


Marcel

Öö Tiib

unread,
Oct 27, 2017, 12:51:19 PM10/27/17
to
On Friday, 27 October 2017 19:11:48 UTC+3, Richard wrote:
>
> Did you find a way to have such a smart pointer class for your
> intrusive reference counted classes in such a way that you could do
> type erasure and only forward declare B?

What is wrong with boost::intrusive_ptr that only needs such things:

namespace richard
{
class ForwardDeclared;
void intrusive_ptr_add_ref(ForwardDeclared* p);
void intrusive_ptr_release(ForwardDeclared* p);
}

boost::intrusive_ptr<richard::ForwardDeclared> that;

There it is made business of ForwardDeclared and those two functions
exactly where (and how thread safely) the references are counted and
what happens when the count drops to zero.

Richard

unread,
Oct 27, 2017, 1:17:43 PM10/27/17
to
[Please do not mail me a copy of your followup]

Marcel Mueller <news.5...@spamgourmet.org> spake the secret code
<osvnqc$1qf$1...@gwaiyur.mb-net.net> thusly:

>In some way I must have managed this years ago.
>Maybe the trick was that the destructor of Location wasn't inline. So
>the destructor of the intrusive pointer was only instantiated in the
>/implementation/ of Location, where all include files have been parsed
>and the type PlayableInstance is complete. Unfortunately a quick sight
>of the old source disproved the statement. The destructor was declared
>in the class header though virtual.
>No idea why this compiles. But I am sure that the type is really
>incomplete during the definition of class Location because the header of
>class PlayableInstance strongly depends on Location.

I'll see if I can come up with a minimal example. In this particular
code base VS 2008 didn't complain, but VS 2017 started complaining.
They changed some stuff with the way templates work in between those
two releases. VS 2008 parses the template declaration but takes the
definition as a stream of tokens that are replayed at instantiation.
This leads to some breakage with respect to so-called "two phase name
lookup" for templates. This blog post goes into more details:
<https://blogs.msdn.microsoft.com/vcblog/2017/09/11/two-phase-name-lookup-support-comes-to-msvc/>
0 new messages