I have a std::shared_ptr
with a custom deleter and in that deleter, I would like to take a temporary copy of the original std::shared_ptr
. Expressed in code form:
struct Foo : public std::enable_shared_from_this<Foo>
{};
void deleter(Foo *f)
{
{
std::shared_ptr<Foo> tmp = f->shared_from_this(); // Line A
}
delete f;
}
int main()
{
std::shared_ptr<Foo> foo(new Foo, &deleter);
}
My question is: on line A, can anything be said about the call of shared_from_this()
? Is it legal? If so, does the standard say anything about its return value? If we replace enable_shared_from_this
with a different weak_ptr
or global reference to foo
, will the answer be the same?
Clang with libc++ and gcc with libstdc++ both produce code which terminates on a bad_weak_ptr
exception, but I can't seem to trace this as required by the standard. Is this implementation-specific, or am I missing a rule?
All the relevant rules I found (quoting C++11):
20.7.2.2.2
shared_ptr
destructor1 ... if
*this
owns an objectp
and a deleterd
,d(p)
is called
2 [Note: ... Since the destruction of*this
decreases the number of instances that share ownership with*this
by one, after*this
has been destroyed allshared_ptr
instances that shared ownership with*this
will report ause_count()
that is one less than its previous value. —end note]20.7.2.2.5
shared_ptr
observers7
use_count
Returns: the number ofshared_ptr
objects,*this
included, that share ownership with*this
, or 0 when*this
is empty.
To me, it seems it's not clear whether the decrement of use_count
happens before or after the call of the deleter. Is getting bad_weak_ptr
a reliable result, or is this simply unspecified?
Note that I am intentionally avoiding a situation where a pointer like tmp
in my example code would outlive the deleter execution.
consider
[c++14-12.4-15]Once a destructor is invoked for an object, the object no longer exists;
and
[c++14-20.8.2.4-7]shared_from_this()[...]Requires: enable_shared_from_this shall be an accessible base class of T. *this shall be a subobject of an object t of type T. There shall be at least one shared_ptr instance p that owns &t.
so, considering that the deleter is invoked by the shared_ptr destructor, calling shared_from_this() in the deleter of the last shared_ptr owning it results in undefined behaviour.
EDIT: as pointed out by YSC, in C++17 shared_from_this() is required to behave as the corresponding weak_ptr conversion call. This complicates matters though, because it's not clear what weak_ptr::expired() should return at deleter call ... anyway, taking the quoted 20.7.2.2.2
note literally, a bad_weak_ptr should be raised in this case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With