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 destructor
1 ... if *this owns an object p and a deleter d, 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 all shared_ptr instances that shared ownership with *this will
report a use_count() that is one less than its previous value. —end note]
20.7.2.2.5 shared_ptr observers
7 use_count Returns: the number of shared_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.
Related
Mr. Lidström and I had an argument :)
Mr. Lidström's claim is that a construct shared_ptr<Base> p(new Derived); doesn't require Base to have a virtual destructor:
Armen Tsirunyan: "Really? Will the shared_ptr clean up correctly? Could you please in this case demonstrate how that effect could be implemented?"
Daniel Lidström: "The shared_ptr uses its own destructor to delete the Concrete instance. This is known as RAII within the C++ community. My advice is that you learn all you can about RAII. It will make your C++ coding so much easier when you use RAII in all situations."
Armen Tsirunyan: "I know about RAII, and I also know that eventually the shared_ptr destructor may delete the stored px when pn reaches 0. But if px had static type pointer to Base and dynamic type pointer to Derived, then unless Base has a virtual destructor, this will result in undefined behavior. Correct me if I am wrong."
Daniel Lidström: "The shared_ptr knows the static type is Concrete. It knows this since I passed it in its constructor! Seems a bit like magic, but I can assure you it is by design and extremely nice."
So, judge us. How is it possible (if it is) to implement shared_ptr without requiring polymorphic classes to have virtual destructor?
Yes, it is possible to implement shared_ptr that way. Boost does and the C++11 standard also requires this behaviour. As an added flexibility shared_ptr manages more than just a reference counter. A so-called deleter is usually put into the same memory block that also contains the reference counters. But the fun part is that the type of this deleter is not part of the shared_ptr type. This is called "type erasure" and is basically the same technique used for implementing the "polymorphic functions" boost::function or std::function for hiding the actual functor's type. To make your example work, we need a templated constructor:
template<class T>
class shared_ptr
{
public:
...
template<class Y>
explicit shared_ptr(Y* p);
...
};
So, if you use this with your classes Base and Derived ...
class Base {};
class Derived : public Base {};
int main() {
shared_ptr<Base> sp (new Derived);
}
... the templated constructor with Y=Derived is used to construct the shared_ptr object. The constructor has thus the chance to create the appropriate deleter object and reference counters and stores a pointer to this control block as a data member. If the reference counter reaches zero, the previously created and Derived-aware deleter will be used to dispose of the object.
The C++11 standard has the following to say about this constructor (20.7.2.2.1):
Requires: p must be convertible to T*. Y shall be a complete type. The expression delete p shall be well formed, shall have well defined behaviour and shall not throw exceptions.
Effects: Constructs a shared_ptr object that owns the pointer p.
…
And for the destructor (20.7.2.2.2):
Effects: If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.
Otherwise, if *this owns an object p and a deleter d, d(p) is called.
Otherwise, if *this owns a pointer p, and delete p is called.
(emphasis using bold font is mine).
When shared_ptr is created it stores a deleter object inside itself. This object is called when the shared_ptr is about to free the pointed resource. Since you know how to destroy the resource at the point of construction you can use shared_ptr with incomplete types. Whoever created the shared_ptr stored a correct deleter there.
For example, you can create a custom deleter:
void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed.
shared_ptr<Base> p(new Derived, DeleteDerived);
p will call DeleteDerived to destroy the pointed object. The implementation does this automatically.
Simply,
shared_ptr uses special deleter function that is created by constructor that always uses
the destructor of the given object and not the destructor of Base, this is a bit of work with template meta programming, but it works.
Something like that
template<typename SomeType>
shared_ptr(SomeType *p)
{
this->destroyer = destroyer_function<SomeType>(p);
...
}
Mr. Lidström and I had an argument :)
Mr. Lidström's claim is that a construct shared_ptr<Base> p(new Derived); doesn't require Base to have a virtual destructor:
Armen Tsirunyan: "Really? Will the shared_ptr clean up correctly? Could you please in this case demonstrate how that effect could be implemented?"
Daniel Lidström: "The shared_ptr uses its own destructor to delete the Concrete instance. This is known as RAII within the C++ community. My advice is that you learn all you can about RAII. It will make your C++ coding so much easier when you use RAII in all situations."
Armen Tsirunyan: "I know about RAII, and I also know that eventually the shared_ptr destructor may delete the stored px when pn reaches 0. But if px had static type pointer to Base and dynamic type pointer to Derived, then unless Base has a virtual destructor, this will result in undefined behavior. Correct me if I am wrong."
Daniel Lidström: "The shared_ptr knows the static type is Concrete. It knows this since I passed it in its constructor! Seems a bit like magic, but I can assure you it is by design and extremely nice."
So, judge us. How is it possible (if it is) to implement shared_ptr without requiring polymorphic classes to have virtual destructor?
Yes, it is possible to implement shared_ptr that way. Boost does and the C++11 standard also requires this behaviour. As an added flexibility shared_ptr manages more than just a reference counter. A so-called deleter is usually put into the same memory block that also contains the reference counters. But the fun part is that the type of this deleter is not part of the shared_ptr type. This is called "type erasure" and is basically the same technique used for implementing the "polymorphic functions" boost::function or std::function for hiding the actual functor's type. To make your example work, we need a templated constructor:
template<class T>
class shared_ptr
{
public:
...
template<class Y>
explicit shared_ptr(Y* p);
...
};
So, if you use this with your classes Base and Derived ...
class Base {};
class Derived : public Base {};
int main() {
shared_ptr<Base> sp (new Derived);
}
... the templated constructor with Y=Derived is used to construct the shared_ptr object. The constructor has thus the chance to create the appropriate deleter object and reference counters and stores a pointer to this control block as a data member. If the reference counter reaches zero, the previously created and Derived-aware deleter will be used to dispose of the object.
The C++11 standard has the following to say about this constructor (20.7.2.2.1):
Requires: p must be convertible to T*. Y shall be a complete type. The expression delete p shall be well formed, shall have well defined behaviour and shall not throw exceptions.
Effects: Constructs a shared_ptr object that owns the pointer p.
…
And for the destructor (20.7.2.2.2):
Effects: If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.
Otherwise, if *this owns an object p and a deleter d, d(p) is called.
Otherwise, if *this owns a pointer p, and delete p is called.
(emphasis using bold font is mine).
When shared_ptr is created it stores a deleter object inside itself. This object is called when the shared_ptr is about to free the pointed resource. Since you know how to destroy the resource at the point of construction you can use shared_ptr with incomplete types. Whoever created the shared_ptr stored a correct deleter there.
For example, you can create a custom deleter:
void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed.
shared_ptr<Base> p(new Derived, DeleteDerived);
p will call DeleteDerived to destroy the pointed object. The implementation does this automatically.
Simply,
shared_ptr uses special deleter function that is created by constructor that always uses
the destructor of the given object and not the destructor of Base, this is a bit of work with template meta programming, but it works.
Something like that
template<typename SomeType>
shared_ptr(SomeType *p)
{
this->destroyer = destroyer_function<SomeType>(p);
...
}
Mr. Lidström and I had an argument :)
Mr. Lidström's claim is that a construct shared_ptr<Base> p(new Derived); doesn't require Base to have a virtual destructor:
Armen Tsirunyan: "Really? Will the shared_ptr clean up correctly? Could you please in this case demonstrate how that effect could be implemented?"
Daniel Lidström: "The shared_ptr uses its own destructor to delete the Concrete instance. This is known as RAII within the C++ community. My advice is that you learn all you can about RAII. It will make your C++ coding so much easier when you use RAII in all situations."
Armen Tsirunyan: "I know about RAII, and I also know that eventually the shared_ptr destructor may delete the stored px when pn reaches 0. But if px had static type pointer to Base and dynamic type pointer to Derived, then unless Base has a virtual destructor, this will result in undefined behavior. Correct me if I am wrong."
Daniel Lidström: "The shared_ptr knows the static type is Concrete. It knows this since I passed it in its constructor! Seems a bit like magic, but I can assure you it is by design and extremely nice."
So, judge us. How is it possible (if it is) to implement shared_ptr without requiring polymorphic classes to have virtual destructor?
Yes, it is possible to implement shared_ptr that way. Boost does and the C++11 standard also requires this behaviour. As an added flexibility shared_ptr manages more than just a reference counter. A so-called deleter is usually put into the same memory block that also contains the reference counters. But the fun part is that the type of this deleter is not part of the shared_ptr type. This is called "type erasure" and is basically the same technique used for implementing the "polymorphic functions" boost::function or std::function for hiding the actual functor's type. To make your example work, we need a templated constructor:
template<class T>
class shared_ptr
{
public:
...
template<class Y>
explicit shared_ptr(Y* p);
...
};
So, if you use this with your classes Base and Derived ...
class Base {};
class Derived : public Base {};
int main() {
shared_ptr<Base> sp (new Derived);
}
... the templated constructor with Y=Derived is used to construct the shared_ptr object. The constructor has thus the chance to create the appropriate deleter object and reference counters and stores a pointer to this control block as a data member. If the reference counter reaches zero, the previously created and Derived-aware deleter will be used to dispose of the object.
The C++11 standard has the following to say about this constructor (20.7.2.2.1):
Requires: p must be convertible to T*. Y shall be a complete type. The expression delete p shall be well formed, shall have well defined behaviour and shall not throw exceptions.
Effects: Constructs a shared_ptr object that owns the pointer p.
…
And for the destructor (20.7.2.2.2):
Effects: If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.
Otherwise, if *this owns an object p and a deleter d, d(p) is called.
Otherwise, if *this owns a pointer p, and delete p is called.
(emphasis using bold font is mine).
When shared_ptr is created it stores a deleter object inside itself. This object is called when the shared_ptr is about to free the pointed resource. Since you know how to destroy the resource at the point of construction you can use shared_ptr with incomplete types. Whoever created the shared_ptr stored a correct deleter there.
For example, you can create a custom deleter:
void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed.
shared_ptr<Base> p(new Derived, DeleteDerived);
p will call DeleteDerived to destroy the pointed object. The implementation does this automatically.
Simply,
shared_ptr uses special deleter function that is created by constructor that always uses
the destructor of the given object and not the destructor of Base, this is a bit of work with template meta programming, but it works.
Something like that
template<typename SomeType>
shared_ptr(SomeType *p)
{
this->destroyer = destroyer_function<SomeType>(p);
...
}
Mr. Lidström and I had an argument :)
Mr. Lidström's claim is that a construct shared_ptr<Base> p(new Derived); doesn't require Base to have a virtual destructor:
Armen Tsirunyan: "Really? Will the shared_ptr clean up correctly? Could you please in this case demonstrate how that effect could be implemented?"
Daniel Lidström: "The shared_ptr uses its own destructor to delete the Concrete instance. This is known as RAII within the C++ community. My advice is that you learn all you can about RAII. It will make your C++ coding so much easier when you use RAII in all situations."
Armen Tsirunyan: "I know about RAII, and I also know that eventually the shared_ptr destructor may delete the stored px when pn reaches 0. But if px had static type pointer to Base and dynamic type pointer to Derived, then unless Base has a virtual destructor, this will result in undefined behavior. Correct me if I am wrong."
Daniel Lidström: "The shared_ptr knows the static type is Concrete. It knows this since I passed it in its constructor! Seems a bit like magic, but I can assure you it is by design and extremely nice."
So, judge us. How is it possible (if it is) to implement shared_ptr without requiring polymorphic classes to have virtual destructor?
Yes, it is possible to implement shared_ptr that way. Boost does and the C++11 standard also requires this behaviour. As an added flexibility shared_ptr manages more than just a reference counter. A so-called deleter is usually put into the same memory block that also contains the reference counters. But the fun part is that the type of this deleter is not part of the shared_ptr type. This is called "type erasure" and is basically the same technique used for implementing the "polymorphic functions" boost::function or std::function for hiding the actual functor's type. To make your example work, we need a templated constructor:
template<class T>
class shared_ptr
{
public:
...
template<class Y>
explicit shared_ptr(Y* p);
...
};
So, if you use this with your classes Base and Derived ...
class Base {};
class Derived : public Base {};
int main() {
shared_ptr<Base> sp (new Derived);
}
... the templated constructor with Y=Derived is used to construct the shared_ptr object. The constructor has thus the chance to create the appropriate deleter object and reference counters and stores a pointer to this control block as a data member. If the reference counter reaches zero, the previously created and Derived-aware deleter will be used to dispose of the object.
The C++11 standard has the following to say about this constructor (20.7.2.2.1):
Requires: p must be convertible to T*. Y shall be a complete type. The expression delete p shall be well formed, shall have well defined behaviour and shall not throw exceptions.
Effects: Constructs a shared_ptr object that owns the pointer p.
…
And for the destructor (20.7.2.2.2):
Effects: If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.
Otherwise, if *this owns an object p and a deleter d, d(p) is called.
Otherwise, if *this owns a pointer p, and delete p is called.
(emphasis using bold font is mine).
When shared_ptr is created it stores a deleter object inside itself. This object is called when the shared_ptr is about to free the pointed resource. Since you know how to destroy the resource at the point of construction you can use shared_ptr with incomplete types. Whoever created the shared_ptr stored a correct deleter there.
For example, you can create a custom deleter:
void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed.
shared_ptr<Base> p(new Derived, DeleteDerived);
p will call DeleteDerived to destroy the pointed object. The implementation does this automatically.
Simply,
shared_ptr uses special deleter function that is created by constructor that always uses
the destructor of the given object and not the destructor of Base, this is a bit of work with template meta programming, but it works.
Something like that
template<typename SomeType>
shared_ptr(SomeType *p)
{
this->destroyer = destroyer_function<SomeType>(p);
...
}
Mr. Lidström and I had an argument :)
Mr. Lidström's claim is that a construct shared_ptr<Base> p(new Derived); doesn't require Base to have a virtual destructor:
Armen Tsirunyan: "Really? Will the shared_ptr clean up correctly? Could you please in this case demonstrate how that effect could be implemented?"
Daniel Lidström: "The shared_ptr uses its own destructor to delete the Concrete instance. This is known as RAII within the C++ community. My advice is that you learn all you can about RAII. It will make your C++ coding so much easier when you use RAII in all situations."
Armen Tsirunyan: "I know about RAII, and I also know that eventually the shared_ptr destructor may delete the stored px when pn reaches 0. But if px had static type pointer to Base and dynamic type pointer to Derived, then unless Base has a virtual destructor, this will result in undefined behavior. Correct me if I am wrong."
Daniel Lidström: "The shared_ptr knows the static type is Concrete. It knows this since I passed it in its constructor! Seems a bit like magic, but I can assure you it is by design and extremely nice."
So, judge us. How is it possible (if it is) to implement shared_ptr without requiring polymorphic classes to have virtual destructor?
Yes, it is possible to implement shared_ptr that way. Boost does and the C++11 standard also requires this behaviour. As an added flexibility shared_ptr manages more than just a reference counter. A so-called deleter is usually put into the same memory block that also contains the reference counters. But the fun part is that the type of this deleter is not part of the shared_ptr type. This is called "type erasure" and is basically the same technique used for implementing the "polymorphic functions" boost::function or std::function for hiding the actual functor's type. To make your example work, we need a templated constructor:
template<class T>
class shared_ptr
{
public:
...
template<class Y>
explicit shared_ptr(Y* p);
...
};
So, if you use this with your classes Base and Derived ...
class Base {};
class Derived : public Base {};
int main() {
shared_ptr<Base> sp (new Derived);
}
... the templated constructor with Y=Derived is used to construct the shared_ptr object. The constructor has thus the chance to create the appropriate deleter object and reference counters and stores a pointer to this control block as a data member. If the reference counter reaches zero, the previously created and Derived-aware deleter will be used to dispose of the object.
The C++11 standard has the following to say about this constructor (20.7.2.2.1):
Requires: p must be convertible to T*. Y shall be a complete type. The expression delete p shall be well formed, shall have well defined behaviour and shall not throw exceptions.
Effects: Constructs a shared_ptr object that owns the pointer p.
…
And for the destructor (20.7.2.2.2):
Effects: If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.
Otherwise, if *this owns an object p and a deleter d, d(p) is called.
Otherwise, if *this owns a pointer p, and delete p is called.
(emphasis using bold font is mine).
When shared_ptr is created it stores a deleter object inside itself. This object is called when the shared_ptr is about to free the pointed resource. Since you know how to destroy the resource at the point of construction you can use shared_ptr with incomplete types. Whoever created the shared_ptr stored a correct deleter there.
For example, you can create a custom deleter:
void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed.
shared_ptr<Base> p(new Derived, DeleteDerived);
p will call DeleteDerived to destroy the pointed object. The implementation does this automatically.
Simply,
shared_ptr uses special deleter function that is created by constructor that always uses
the destructor of the given object and not the destructor of Base, this is a bit of work with template meta programming, but it works.
Something like that
template<typename SomeType>
shared_ptr(SomeType *p)
{
this->destroyer = destroyer_function<SomeType>(p);
...
}