Why calling shared_from_this calls std::terminate - c++

Consider this code:
class A : public std::enable_shared_from_this<A>
{
public:
std::shared_ptr<A> f()
{
return shared_from_this();
}
};
int main()
{
A a;
std::shared_ptr<A> ptr = a.f();
}
This code terminated in Visual Studio 2017. I guess I am doing something wrong here. Can anyone help me with this? I want a shared_ptr on a created by shared_from_this().

Because a is not a owned by a shared pointer. From cppreference:
It is permitted to call shared_from_this only on a previously shared object, i.e. on an object managed by std::shared_ptr. Otherwise the behavior is undefined (until C++17)std::bad_weak_ptr is thrown (by the shared_ptr constructor from a default-constructed weak_this) (since C++17).

You cannot use shared_from_this to generate a new shared pointer. You already have to have an existing shared pointer to get a new one:
std::shared_ptr a(new A());
auto ptr = a->f(); // ok, 'ptr' shares ownership of newed object with 'a'

The issue is fundamental in the design (not technical details)
Whatever the exact specification of that C++ standard version you are using, what you are trying to do is impossible. Knowing the fine details of the specification of shared_from_this isn't needed to conclude that the code contains a design contradiction: just understanding the intent, which is to obtain a shared_ptr<A> to this, inside a member function called on a, an automatic object, is enough to determine that the design is in error.
In fact, any attempt at making an owning smart pointer (incl. but not limited to unique_ptr, shared_ptr) that points to (that owns) an object with "scoped" lifetime, that is an object whose lifetime is defined by the scope of the declaration of the object and ended by exiting something:
automatic objects (lifetime ends when exiting the scope),
namespace scope objects, static object members of classes (lifetime ends at program exit),
non static members of classes (lifetime ends when the body of the destructor of the containing class object exits),
is a design error, because the:
these objects were not created with any variant new (plain operator new or nothrow variant) that permits the call of delete on the result);
the only case where C++ allows you to destroy such object is by destruction followed (preferably immediately) by reconstruction with placement new of an object with the same complete type, which is obviously not the job of an owning smart pointer;
the compiler will destroy that object when program execution reach the exit point (exits scope, exits destructor, or std::exit, or return from main), no matter what (even if your owning smart pointer already took care of it); trying to destroy an already destroy object is not OK.
This includes constructing a smart pointer that owns (i.e. that promises to delete) a member of a class instance that was dynamically allocated:
struct A {
int m;
};
#define OK 1
void f() {
A *p = new A;
#if OK
std::shared_ptr<A> own (p); // fine
#else
std::shared_ptr<int> own (&p->m); // bad
#endif
}
Here the lifetime of the object pointed to by p is managed dynamically; the timing of the destruction of determined explicitly, by program code, and the lifetime of the unique member m is intrinsically linked to the lifetime of the A object; but the member itself need not be destructed explicitly and shall not be deleted. If the OK preprocessor constant is 1, all is well; if it is 0, you are trying to manage explicitly the lifetime of a member, which is unsound.
About the term "explicit" call to delete: although th delete operator never appears in the code, its call is implicit on the use of std::shared_ptr; in other words, std::shared_ptr explicitly uses delete, so use of std::shared_ptr (or other similar owning smart pointers) are indirect use of delete.
Safely sharing ownership with a smart pointer
The only safe way to share ownership of a shared_ptr is to make one from another shared_ptr, directly, or indirectly. This is the fundamental property of shared_ptr: all instances pointing to the one object must be traceable back to the one instance that one constructed with a raw pointer (or alternatively with make_shared).
This is a direct consequence of the fact that the ownership information (usually a reference count, but could be a linked list if you love inefficient implementations) is not inside the managed object, but inside the information block created by shared_ptr. This is not a property of just std::shared_ptr, it's a fact of life of all these externally managed objects, without a global registry, it's impossible to find the manager.
The basic design decision of these smart pointer is that the managed object need not be modified to use a smart pointer; hence they can be used on existing data type (incl. fundamental types).
Importance of weak copies of a shared owning manager
The fundamental property of shared_ptr would create an issue: as every layer of code (that might need to call a function that needs an owning pointer) needs to keep a copy of the shared_ptr around, this can create a web of owning smart pointers, some of which might reside in an object whose lifetime is managed by another who lifetime is managed by that exact smart pointer; because the smart pointer basic specification is that the managed object is not destructed before all copies of the smart pointer in charge of its destruction are destroyed, these objects would never be destroyed (as specified; this is not a consequence of the particular implementation choice of reference counting). Sometimes a copy of a owning smart pointer of a specie that doesn't prevent influence the lifetime of the managed object is needed, hence the need for the weak smart pointer.
A (non null) weak smart pointer is always directly or indirectly a copy of an owning smart pointer, directly or indirectly a copy the original smart pointer that took ownership. That "weak reference" actually is a "strong" owning smart pointer to the information regarding the existence of other owning copies of the smart pointer: as long as there is a weak smart pointer, it will be possible to determine whether there is a live owning smart pointer, and if so to obtain a copy, that is make a shared smart pointer that an exact copy of the original (the lifetime of the original may have ended many generations of copies ago).
The only purpose of a weak smart pointer is to obtain such copies of the original.
The purpose of std::enable_shared_from_this
The only use of std::enable_shared_from_this is to obtain a copy of the original shared_ptr; that implies that such owning smart pointer must already exist. No new original (another smart pointer taking ownership) will be made.
Only use std::enable_shared_from_this for classes that are only intended to be managed by a shared_ptr.
Details of std::enable_shared_from_this
All that being said about the theoretical principles, it's useful to understand what std::enable_shared_from_this contains, how it can produce a shared_ptr when used correctly (and why it cannot be expected to work in any other case).
The "magic" of std::enable_shared_from_this may seem mysterious, and too magic so that users don't have to think about it, but it's actually extremely simple: it keeps a weak_ptr intended to be a copy of the original. Obviously it cannot be constructed as such copy, as the original cannot even be initialized when the std::enable_shared_from_this subobject is constructed: a valid owning smart pointer can only refer to a fully constructed object, because it owns it and is in charge of its destruction. [Even if by some cheating an owning smart pointer was made before the managed object was fully constructed, and hence destructible, the owning smart pointer would be a risk of premature destruction (even if during the normal course of events its lifetime is long, it could be shortened by an exception for example).]
So data member initialization in std::enable_shared_from_this is inherently default initialization: the "weak pointer" is null at that point.
Only when the original finally takes ownership if the managed object, it can collude with std::enable_shared_from_this: the constructing of the original shared_ptr will set once and for all the weak_ptr member inside std::enable_shared_from_this. Active collusion between these components is the only way to make the stuff work.
It's still the user's responsibility to only call shared_from_this only when it can possibly return a copy of the original, that is, after the original has been constructed.
About fake (non owning) owning smart pointers
A fake owning smart pointer is one that does no cleanup ever: owning smart pointer in name only. They are special case of "owning" smart pointers used in such a way that no destruction or cleanup is performed. This ostensibly means that they could be used for objects whose lifetime is predetermined (and long enough) and for which there is a need to have a pretend owning smart pointer; unlike a real owning smart pointer, keeping a copy will not extend the lifetime of the object, so that lifetime should better be really long. (Because a copy of a owning smart pointer could be stored in a global variable, the object could still be expected to be alive after a return from main.)
These non owning owners are obviously a contradiction in the terms and rarely safe (but can be proven safe in a few cases).
They rarely solve a legitimate problem (one that isn't the immediate consequence of a very bad design): a shared_ptr in an interface means that the receiver is expecting to be able to extend the lifetime of the managed object.

Related

Member Objects as unique_ptr or on Stack

Currently i am thinking about storing some member objects in unique_ptr.
Normally i use unique_ptr just in some functions and move it around, or pass references to it.
So how should i do in classes?
Class MyClass {
std::unique_ptr<MyMemberClass> member;
}
or:
Class MyClass {
MyMemberClass member;
}
The lifetime of the object is the same. The initialization in the constructor would be nearly the same.
The only difference is, that i can't copy the unique_ptr, or?
Are there other differences?
And which should i use or prefer?
Are there other differences? And which should i use or prefer?
Yes.
The deciding factors here are:
polymorphic behavior : If you store the common interface for a class hierarchy, then you should store by pointer, smart pointer or reference.
lifetime : as you mentioned, if the held object has a longer lifetime than MyClass (and obviously, it is not owned by MyClass) then it should be held in a raw pointer or a std::shared_ptr.
ownership : if the object is not owned by MyClass, then it should be stored as a pointer (smart or not) or a reference.
api constraints: if the held object is generated by a library that can only allocate it dynamically (for example), then you will probably want to hold it in a pointer (or unique_ptr).
When the object is owned (exclussively) by MyClass, it has the same lifetime as the owning class, and you do not have polymorphic behavior, you should probably store the instance directly.
Differences:
1) You can't copy class with unique_ptr (you can actually, but this has no sense), but you can move.
2) If member is unique_ptr, MyMemberClass object should be allocated on heap, when in second case it's allocated on stack (when object of type MyClass is allocated on stack).
3) If member is complicated object, than probably store pointer on it is better, in other case just object is fine.
4) If member is unique_ptr it can be a polymorphic object. It allows to create an aggregate object. With the second form it is not possible (thanks to chmike).

How does unique pointer ensure uniqueness?

std::unique_ptr is a smart pointer that retains sole ownership of an object through a pointer and destroys that object when the unique_ptr goes out of scope. No two unique_ptr instances can manage the same object.
How the last statement is ensured?
I don't believe that there is "someone" is STL who checks if one of the already existing std::unique_ptrs already own the raw pointer. This would be very inefficient with huge number of unique pointers, even if it is a linear complexity algorithm. There should be a nice trick, right?
It isn't ensured. The name is a statement of intended usage, not any guarantee fully enforced by a runtime system. That is, you can write this code:
std::unique_ptr<int> i1(new int());
std::unique_ptr<int> i2(i1.get());
and you have two unique_ptrs referring to the same object, but the program has undefined behavior because it will delete the pointer twice.
unique_ptr is not copyable to make it harder to create two such pointers by accident. C++ protects against Murphy, not Machiavelli.
The reason it's called unique is because you can't copy a unique pointer. You can steal its value, but that leaves the original unique_ptr empty.

singleton pattern and std::unique_ptr

std::unique_ptr uniquely controls the object it points to and, hence, does not utilize reference counting. A singleton ensures only one object may be created utilizing reference counting.
Would then std::unique_ptr perform identically to a singleton?
A singleton ensures only one instance of a type.
A unique_ptr ensures only one smart pointer to any instance.
Would then std::unique_ptr perform identically to a singleton?
No. Let's say we have class Foo that's intended to be a singleton. Using a typical singleton pattern, there's no way to construct more than one Foo.
Having a std::unique_ptr<Foo> means there will be one pointer to a particular instance of Foo, but that doesn't prevent the creation of other instances of Foo (either with other unique_ptrs or with raw pointers to local variables). Thus Foo, wouldn't be a singleton.
std::unique_ptr achieves single ownership semantics by only providing a move constructor and no copy constructor or assignment operator.
It is not a case of singleton at all, since you can have multiple unique_ptrs referencing different instances of the same type. A singleton doesn't let you construct the type directly, but provides an accessor that manages a sole instance.
Also, Drew's assertion that
"A unique_ptr ensures only one smart pointer to any instance."
is false. If you simply do:
T* nt = new T;
std::unique_ptr<T> up1(nt);
std::unique_ptr<T> up2(nt);
then you have two unique pointers owning the same resource - and you will only notice a problem at run time, not compile time. Of course, this is incorrect usage of unique_ptr, but this reinforces that a unique_ptr does not ensure you anything, it is simply a pointer container that holds sole ownership from its own perspective, and through its api, makes it hard to accidentally create temporary copies.
Furthermore, you can have other (smart) pointer types pointing to the same raw pointer/resource independently of any unique_ptr. It is completely up to the using code to define ownership and lifetime policies of its resources and smart pointer instances
Correct me if I'm wrong, but as far as I remember a singelton is a class which can only have one instance. That's completely different. So no.

Can I use placement new to reset an object within a shared_ptr?

Let's say I have a class.
class BigData {...};
typedef boost::shared_ptr<BigData> BigDataPtr;
Then I do:
BigDataPtr bigDataPtr(new BigData());
Later on after I am done with my object and I am sure there no other users for the object.
Is it safe to do the following:
bigDataPtr->~BigDataPtr();
new (&*bigDataPtr) BigData;
Would this let me reset the object without any additional allocations?
There are a few ways to go about this. You can use placement new, and this is guaranteed to be safe for two reasons:
You have already allocated the memory for the object, so you know it’s sized and aligned correctly.
shared_ptr is non-invasive; its sole responsibility is to count references and call the deleter when necessary.
However, consider what can happen if reconstruction of the object fails—i.e., throws an exception:
bigDataPtr->~BigDataPtr();
new (bigDataPtr.get()) BigData;
Then you have a problem: the deleter can be called on a non-constructed object, leading almost certainly to undefined behaviour. I say “almost” because the deleter could be a no-op, in which case all would be well.
Safer, I think, would be to move a new value into the existing object:
*bigDataPtr = BigData(42);
Or add a reset() member function to BigData:
bigDataPtr->reset(42);
Then it’s explicit what your real intent is, and you don’t need to be as concerned about object lifetimes.
Yes it is normally safe. (Nod to Maxim Yegorushkin's observation about a throwing edge case)
Note the typo message below
Boost defines the dereference and -> operators as
template<class T>
typename boost::detail::sp_dereference< T >::type boost::shared_ptr< T >::operator* () const;
template<class T>
typename boost::detail::sp_member_access< T >::type boost::shared_ptr< T >::operator-> () const;
When those detail bits are resolved, you have this
template<class T>
T & boost::shared_ptr< T >::operator* () const
template<class T>
T * boost::shared_ptr< T >::operator-> () const
So you are dealing with the pointed-to object directly. There are no proxies or other constructs that may interfere with what you're attempting.
As the pointed-to data is concerned, your code:
bigDataPtr->~BigDataPtr();
new (&*bigDataPtr) BigData;
May have a typo. But if you intended:
bigDataPtr->~BigData();
new (&*bigDataPtr) BigData;
It will resolve to
(BigData pointer)->~BigData();
new (&(BigData reference)) BigData;
This is legal, and you are correct that it would avoid the additional allocation normally incurred with an assignment.
It is safe if BigData constructor and destructor do not throw exceptions and bigDataPtr is not shared between threads and no pointers or references exist to dynamically allocated members of BigData (if any).
If the destructor throws an exception you may end up with a partially destroyed object (throwing destructors are not generally recommended and standard containers require that destructors of elements do not throw).
If the constructor throws you may end up destroying the object but not constructing a new one.
If bigDataPtr is shared between threads that may also lead to a race condition unless a locking discipline is used.
If code elsewhere takes references or pointers to dynamically allocated members of BigData, when it creates a new BigData its dynamically allocated members may be allocated at other addresses, so existing pointers and references to the members become invalid.
If you are concerned with dubious dereference in new (&*bigDataPtr) BigData; statement use a plain pointer instead:
BigData* p = bigDataPtr.get();
p->~BigData();
new (p) BigData;
Firstly, if the constructor throws and the class is not trivially destructible then you have a problem, since the shared_ptr "wants" to delete it, which would provoke UB.
So you must deal with that, either by using a nothrow constructor or by catching any exception and preventing the smart pointer from deleting the object. Since shared_ptr doesn't have a release() function, that's easier said than done. You could call terminate() if all else fails, but that won't make you popular with your users.
If there are no other references to the object, then it will work provided that the class has no const or reference non-static data members (including members-of-members). The reason is 3.8/7:
If, after the lifetime of an object has ended and before the storage
which the object occupied is reused or released, a new object is
created at the storage location which the original object occupied, a
pointer that pointed to the original object ... can be used to
manipulate the new object, if ... the type of the original object is
not const-qualified, and, if a class type, does not contain any
non-static data member whose type is const-qualified or a reference
type ...
Note that the shared_ptr holds just such a pointer, which it will use to manipulate the new object, which is UB if any of the conditions in 3.8/7 is broken. The only one that might be broken is this one, you've covered the rest with what you've said about your code. In particular, it's required that you created the original object as an instance of BigData, not a class derived from BigData, because the new object is required to have the same most-derived type as the old one.
There are usually more robust ways to reset an object than this. For example, implement operator= (copy- or move assignment operator) and then write *bigDataPtr = BigData(). Of course that might not be quite as fast.

Can deriving a class from 'enable_shared_from_this' increase performance?

make_shared is more performant than separately calling new and creating a shared_ptr because make_shared allocates space for the reference count and weak count in the same memory block as the client object instance (effectively giving the shared_ptr most of the performance benefits of an intrusive_ptr).
enable_shared_from_this gives a shared pointer without having a reference to any shared pointer. Therefore things like the reference and weak count have to be somehow accessible from inside the client object. Therefore, it would be sensible for enable_shared_from_this to cause an intrusive count similar to make_shared.
However, I have no idea how something like that might be implemented (and I'm not sure I'd follow what was going on in there even if I look at the actual source).
Would it make sense then (for performance reasons) to tag my class with enable_shared_from_this if I know it's only ever going to be used as a shared_ptr and never as a raw object?
I have never dug into the details of implementation, but for shared_from_this to work, the object must already be managed by an external shared_ptr, so it is to some extent unrelated. I.e. the first shared_ptr might have been created with make_shared in which case the count and object are together (as you say intrusive pointer like), but that does not need to be the case.
My first guess is that enable_shared_from_this adds the equivalent of a weak_ptr, rather than a shared_ptr. EDIT: I have just verified the implementation in gcc4.6:
template <typename _Tp>
class enable_shared_from_this {
...
mutable weak_ptr<_Tp> _M_weak_this;
};
I don't believe so. How enable_shared_from_this is implemented isn't strictly defined, but an example implementation is present in the standard that corresponds to how boost does it. Basicaly, there is a hidden weak_ptr that shared_ptr and friends has access to... anytime a shared_ptr is given ownership of an object derived from enable_shared_from_this, it updates that internal pointer. Then shared_from_this() simply returns a strong version of that weak pointer.
In the general case, the implementation can't really assume that nobody will ever go shared_ptr(new T) instead of using make_shared, so an intrusive reference count would be risky. You can instead make that guarentee yourself, by whatever means you use to construct the objects in the first place.
Boost's enable_shared_from_this does not change the implementation of shared_ptr itself. Remember that shared_ptr is paired with weak_ptr - this means that, even after the object is deleted, the tracking data may need to remain around to tell the weak_ptr that the object is dead. As such, it can't be embedded into the object, even with enable_shared_from_this. All enable_shared_from_this does is embed a pointer to said tracking data into the object, so a shared_ptr can be constructed with just a pointer to the object.
This is also why intrusive_ptr cannot have a weak_intrusive_ptr variant.