On 24.12.2016 5:00, Alf P. Steinbach wrote:
> For a restriction to dynamic allocation by making the destructor
> non-`public`, I wonder what would be a good way to support destructor
> invocation via `std::shared_ptr` and other smart pointers?
>
> [code]
> #include <type_traits>
> #include <iostream>
> using namespace std;
>
> #define STATIC_ASSERT( e ) static_assert( e, "`" #e "` <-- must be true" )
>
> namespace cppx{
> template< class Type >
> constexpr auto is_destructible()
> -> bool
> { return std::is_destructible<Type>::value; }
> } // namespace cppx
>
> template< class Derived >
> struct Dynamic_only
> {
> void selfdestroy() { delete this; }
I have had similar desires in the past to demand only dynamic allocation
for some classes and I went to great lengths to develop my own
smartpointers which supported that. However, by now I have come to
conclusion that these desires were futile and actually not needed for
anything.
First note that internal refcounting cannot be made work with
std::shared_ptr in principle, as std::shared_ptr also supports
thread-safe weak pointers, but such weak pointers are not compatible
with internal reference counters (a race condition between weak pointer
mechanisms and the object destructor).
So, if one wants to use std::shared_ptr, one cannot use internal
reference counting. This actually solves one of the reasons why one
might want to prohibit stack allocation - if there is no selfdestroy()
function then there is no danger that somebody might accidentally call
it and try to delete an object which has not been allocated dynamically.
There remains the danger in that a std::shared_ptr can be constructed
from an unusable raw pointer like 'this' or &x (this practice would work
fine in a legacy codebase using some kind of intrusive refcounting, but
not with std::shared_ptr).
For performance reasons we wanted to use std::make_shared() or
std::allocate_shared() anyway instead of plain new, so there was no
point in allowing to construct a std::shared_ptr() from a raw pointer.
Alas, this constructor is there and will not go away. So, in our
codebase, to mitigate this problem, I created another wrapper pointer
class derived from std::shared_ptr which has the constructor from raw
pointer deleted. All the code uses this class rather than
std::shared_ptr, which basically eliminates the possibility of accidents.
As the end result, one can now create local stack objects of those types
and these work fine, one just cannot obtain a valid smartpointer to such
an object (and creating an invalid smartpointer requires some conscious
mischief).