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_ptrdestructor1 ... if
*thisowns an objectpand a deleterd,d(p)is called
2 [Note: ... Since the destruction of*thisdecreases the number of instances that share ownership with*thisby one, after*thishas been destroyed allshared_ptrinstances that shared ownership with*thiswill report ause_count()that is one less than its previous value. —end note]20.7.2.2.5
shared_ptrobservers7
use_countReturns: the number ofshared_ptrobjects,*thisincluded, that share ownership with*this, or 0 when*thisis 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