On Thu, 29 Aug 2019 at 11:50, Christian Schneider
<cschneider@radiodata.biz> wrote:
Am 29.08.19 um 12:07 schrieb Jonathan Wakely:
On Thu, 29 Aug 2019 at 10:15, Christian Schneider
<cschneider@radiodata.biz> wrote:
Hello,
I just discovered, that, when using enable_shared_from_this and
inheriting it privately, this fails at runtime.
I made a small example:
#include <memory>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>
#ifndef prefix
#define prefix std
#endif
class foo:
prefix::enable_shared_from_this<foo>
{
public:
prefix::shared_ptr<foo> get_sptr()
{
return shared_from_this();
}
};
int main()
{
auto a = prefix::make_shared<foo>();
auto b = a->get_sptr();
return 0;
}
This compiles fine, but throws a weak_ptr exception at runtime.
I'm aware, that the implementation requires, that
enable_shared_from_this needs to be publicly inherited, but as a first
time user, I had to find this out the hard way, as documentations (I
use, ie. cppreference.com) don't mention it, probably because it's not a
requirement of the standard.
It definitely is a requirement of the standard. The new wording we
added via http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0033r1.html#spec
says that the base's weak_ptr is only initialized when the base class
is "unambiguous and accessible". It doesn't say that an ambiguous or
inaccessible base class makes the program ill-formed, so we're not
allowed to reject such a program. >
I see. As far as I understand, this sentence was removed:
Requires: enable_shared_from_this<T> 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.
As far as I read it, this required enable_shared_from_this to be public
accessible.
No. It only required it to be publicly accessible if you called
shared_from_this().
Do you know (or someone else), why it was removed?
Yes (look at the author of the paper :-). As I wrote in that paper:
"The proposed wording removes the preconditions on shared_from_this so
that it is now well-defined to call it on an object which is not owned
by any shared_ptr, in which case shared_from_this would throw an
exception."
Previously it was undefined behaviour to call shared_from_this() if
the base class hadn't been initialized to share ownership with a
shared_ptr. That meant the following was undefined:
#include <memory>
struct X : std::enable_shared_from_this<X> { };
int main()
{
X x; // not owned by a shared_ptr
x.shared_from_this();
}
Now this program is perfectly well-defined, but it throws an
exception. There is no good reason to say that program has undefined
behaviour (which means potentially unbounded types of errors) when we
can just make it valid code that throws an exception when misused.