It seems relatively simple to create a wrapper class that can automatically create the necessary shared_ptr in order for enable_shared_from_this to work.
template<typename T>
class Sharable : public std::enable_shared_from_this<T>
{
private:
std::shared_ptr<T> const _sthis; // automatic shared_ptr to THIS
using std::enable_shared_from_this<T>::shared_from_this;
static void NoOpDeleter(T*) noexcept
{
}
public:
Sharable() noexcept :
_sthis{static_cast<T*>(this), NoOpDeleter} // DO NOT delete _sthis, it is freed by ~T()
{
}
// disallow copying this class
Sharable(Sharable const &other) = delete;
Sharable(Sharable &&other) = delete;
virtual ~Sharable() noexcept = default;
Sharable& operator=(Sharable const&) = delete; // disallow assignment
operator std::shared_ptr<T>() noexcept
{
return shared_from_this();
}
long use_count() const
{
return _sthis.use_count();
}
};
// implementing class
template<typename T>
class SharableObject : public Sharable<SharableObject<T>>
{
public:
SharableObject() noexcept = default;
~SharableObject() noexcept = default;
};
I have tested (MinGW 4.8.1) several use cases for this, and AFAICT it properly frees the memory and maintains an accurate use count in all cases (I also added print statements in the destructors, which I've omitted here)...
int main(int, char**)
{
std::shared_ptr<SharableObject<int>> so1{new SharableObject<int>};
SharableObject<int> so2;
{
auto p = so1.get()->shared_from_this();
auto p2 = so2.shared_from_this();
std::cout << std::endl << so1.use_count() << ", " << so2.use_count() << std::endl;
// prints 2, 2
}
std::cout << std::endl << so1.use_count() << ", " << so2.use_count() << std::endl;
// prints 1, 1
return 0;
}
I feel as though I must have missed something or else this would have been provided by enabled_shared_from_this already. It doesn't obfuscate the "normal" shared_ptr syntax for heap allocations, but makes it simple to provide a shared_ptr to the object for stack allocated objects as well (without enforcing the named constructor idiom).
The static_cast from Sharable<T>* to T* should be sufficient to ensure the contract that T derives from (or is a base class of?) Sharable<T>, or else the template could not properly resolve itself into a class (AFAIK). The use of NoOpDeleter prevents the destructor from being invoked multiple times...
So, does anyone have any thoughts on what I've overlooked, or why enable_shared_from_this is not already providing this behavior? The only reason I could potentially see is that they did not want to forcibly include a shared_ptr as a member of every instance, but AFAIK it is already including a weak_ptr instead...? I don't understand why this feature was implemented this way.
Aucun commentaire:
Enregistrer un commentaire