1

Can't std::shared_from_this be inherited by its derived classes?

Why doesn't this code snippet compile(check http://cpp.sh/7llcr)? I have read the documentation(https://en.cppreference.com/w/cpp/memory/enable_shared_from_this) carefully, but still can't find any clue. I have thought about it for a long. I would be very grateful to have some help with this question.

Here is the code snippet which does not compile:

// enable_shared_from_this example
#include <iostream>
#include <memory>

struct C : std::enable_shared_from_this<C> {};

struct D : public C {};

int main () {
  std::shared_ptr<D> foo, bar;

  foo = std::make_shared<D>();

  bar = foo->shared_from_this();


  return 0;
}

Compiler complains:

In file included from /usr/include/c++/4.9/bits/shared_ptr.h:52:0,
                 from /usr/include/c++/4.9/memory:82,
                 from 3:
/usr/include/c++/4.9/bits/shared_ptr_base.h: In instantiation of 'std::__shared_ptr<_Tp, _Lp>& std::__shared_ptr<_Tp, _Lp>::operator=(std::__shared_ptr<_Tp1, _Lp>&&) [with _Tp1 = C; _Tp = D; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]':
/usr/include/c++/4.9/bits/shared_ptr.h:299:4:   required from 'std::shared_ptr<_Tp>& std::shared_ptr<_Tp>::operator=(std::shared_ptr<_Tp1>&&) [with _Tp1 = C; _Tp = D]'
14:7:   required from here
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: error: no matching function for call to 'std::__shared_ptr<D, (__gnu_cxx::_Lock_policy)2u>::__shared_ptr(std::remove_reference<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>::type)'
    __shared_ptr(std::move(__r)).swap(*this);
    ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: note: candidates are:
/usr/include/c++/4.9/bits/shared_ptr_base.h:1145:7: note: std::__shared_ptr<_Tp, _Lp>::__shared_ptr(const std::__weak_ptr<_Tp, _Lp>&, std::nothrow_t) [with _Tp = D; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]
       __shared_ptr(const __weak_ptr<_Tp, _Lp>& __r, std::nothrow_t)
       ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:1145:7: note:   candidate expects 2 arguments, 1 provided
/usr/include/c++/4.9/bits/shared_ptr_base.h:1087:2: note: template<class _Alloc, class ... _Args> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...)
  __shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,
  ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:1087:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: note:   cannot convert 'std::move<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>((* & __r))' (type 'std::remove_reference<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>::type {aka std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>}') to type 'std::_Sp_make_shared_tag'
    __shared_ptr(std::move(__r)).swap(*this);
    ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:967:17: note: constexpr std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::nullptr_t) [with _Tp = D; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u; std::nullptr_t = std::nullptr_t]
       constexpr __shared_ptr(nullptr_t) noexcept
                 ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:967:17: note:   no known conversion for argument 1 from 'std::remove_reference<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>::type {aka std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>}' to 'std::nullptr_t'
/usr/include/c++/4.9/bits/shared_ptr_base.h:963:2: note: template<class _Tp1> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::auto_ptr<_Up>&&)
  __shared_ptr(std::auto_ptr<_Tp1>&& __r);
  ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:963:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: note:   'std::remove_reference<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>::type {aka std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>}' is not derived from 'std::auto_ptr<_Up>'
    __shared_ptr(std::move(__r)).swap(*this);
    ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:951:2: note: template<class _Tp1, class _Del> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::unique_ptr<_Up, _Ep>&&)
  __shared_ptr(std::unique_ptr<_Tp1, _Del>&& __r)
  ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:951:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: note:   'std::remove_reference<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>::type {aka std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>}' is not derived from 'std::unique_ptr<_Tp, _Dp>'
    __shared_ptr(std::move(__r)).swap(*this);
    ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:939:11: note: template<class _Tp1> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(const std::__weak_ptr<_Tp1, _Lp>&)
  explicit __shared_ptr(const __weak_ptr<_Tp1, _Lp>& __r)
           ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:939:11: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: note:   'std::remove_reference<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>::type {aka std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>}' is not derived from 'const std::__weak_ptr<_Tp>'
    __shared_ptr(std::move(__r)).swap(*this);
    ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:931:2: note: template<class _Tp1, class> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::__shared_ptr<_Tp1, _Lp>&&)
  __shared_ptr(__shared_ptr<_Tp1, _Lp>&& __r) noexcept
  ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:931:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:929:31: error: no type named 'type' in 'struct std::enable_if<false, void>'
       template<typename _Tp1, typename = typename
                               ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:922:7: note: std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::__shared_ptr<_Tp, _Lp>&&) [with _Tp = D; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]
       __shared_ptr(__shared_ptr&& __r) noexcept
       ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:922:7: note:   no known conversion for argument 1 from 'std::remove_reference<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>::type {aka std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>}' to 'std::__shared_ptr<D, (__gnu_cxx::_Lock_policy)2u>&&'
/usr/include/c++/4.9/bits/shared_ptr_base.h:918:2: note: template<class _Tp1, class> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(const std::__shared_ptr<_Tp1, _Lp>&)
  __shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r) noexcept
  ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:918:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:916:31: error: no type named 'type' in 'struct std::enable_if<false, void>'
       template<typename _Tp1, typename = typename
                               ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:912:7: note: std::__shared_ptr<_Tp, _Lp>::__shared_ptr(const std::__shared_ptr<_Tp, _Lp>&) [with _Tp = D; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]
       __shared_ptr(const __shared_ptr&) noexcept = default;
       ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:912:7: note:   no known conversion for argument 1 from 'std::remove_reference<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>::type {aka std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>}' to 'const std::__shared_ptr<D, (__gnu_cxx::_Lock_policy)2u>&'
/usr/include/c++/4.9/bits/shared_ptr_base.h:908:2: note: template<class _Tp1> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(const std::__shared_ptr<_Tp1, _Lp>&, _Tp*)
  __shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r, _Tp* __p) noexcept
  ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:908:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: note:   candidate expects 2 arguments, 1 provided
    __shared_ptr(std::move(__r)).swap(*this);
    ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:903:9: note: template<class _Deleter, class _Alloc> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::nullptr_t, _Deleter, _Alloc)
         __shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
         ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:903:9: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: note:   cannot convert 'std::move<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>((* & __r))' (type 'std::remove_reference<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>::type {aka std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>}') to type 'std::nullptr_t'
    __shared_ptr(std::move(__r)).swap(*this);
    ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:898:2: note: template<class _Deleter> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::nullptr_t, _Deleter)
  __shared_ptr(nullptr_t __p, _Deleter __d)
  ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:898:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: note:   cannot convert 'std::move<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>((* & __r))' (type 'std::remove_reference<std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>&>::type {aka std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>}') to type 'std::nullptr_t'
    __shared_ptr(std::move(__r)).swap(*this);
    ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:889:2: note: template<class _Tp1, class _Deleter, class _Alloc> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Tp1*, _Deleter, _Alloc)
  __shared_ptr(_Tp1* __p, _Deleter __d, _Alloc __a)
  ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:889:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: note:   mismatched types '_Tp1*' and 'std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>'
    __shared_ptr(std::move(__r)).swap(*this);
    ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:880:2: note: template<class _Tp1, class _Deleter> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Tp1*, _Deleter)
  __shared_ptr(_Tp1* __p, _Deleter __d)
  ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:880:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: note:   mismatched types '_Tp1*' and 'std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>'
    __shared_ptr(std::move(__r)).swap(*this);
    ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:870:11: note: template<class _Tp1> std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Tp1*)
  explicit __shared_ptr(_Tp1* __p)
           ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:870:11: note:   template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/shared_ptr_base.h:1001:4: note:   mismatched types '_Tp1*' and 'std::__shared_ptr<C, (__gnu_cxx::_Lock_policy)2u>'
    __shared_ptr(std::move(__r)).swap(*this);
    ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:865:17: note: constexpr std::__shared_ptr<_Tp, _Lp>::__shared_ptr() [with _Tp = D; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]
       constexpr __shared_ptr() noexcept
                 ^
/usr/include/c++/4.9/bits/shared_ptr_base.h:865:17: note:   candidate expects 0 arguments, 1 provided
John
  • 1,029
  • 2
  • 14

2 Answers2

4

It is inherited:

// enable_shared_from_this example
#include <iostream>
#include <memory>

struct C : std::enable_shared_from_this<C> {};

struct D : public C {};

int main () {
  std::shared_ptr<D> foo;

  foo = std::make_shared<D>();

  std::shared_ptr<C> bar = foo->shared_from_this();

  return 0;
}

You define the shared_from_this in the class C: so the object returns std::shated_ptr<C>. No surprise. You still may downcast it to std::shared_ptr<D> with std::dynamic_pointer_cast.

Dmitry Kuzminov
  • 4,427
  • 5
  • 12
  • 26
  • Thank you. Why `std::shared_from_this` can't be inherited as the normal classes do? E.G.:`class Base base{public: void* ptr;}; class Drived:public Base {}; int main{Drived d; d->ptr = NULL}`. I wonder what causes it a little strange. – John Jun 11 '20 at 06:25
  • 1
    `std::enable_shared_from_this` is inherited. If `struct D` inherits `std::enable_shared_from_this` from `C` then `D::shared_from_this` comes from `std::enable_shared_from_this` so it will construct `C`. If you want `D::shared_from_this` to return `D` you have to somewhere inherit from `std::enable_shared_from_this`. – KamilCuk Jun 11 '20 at 06:26
  • @KamilCuk I see. Thank you for the clarification. One more question, since `foo` and `bar` are both lvalue, I can get their addresses in theory, but `&bar` and `&foo` don't work(they get the addresses of the newly instantiation of class `D`). How can I get the address of `foo` itself? – John Jun 11 '20 at 06:42
  • 1
    A class can overload `operator&`, but `std::shared_ptr` shouldn't. You can use `std::addressof` to really get the address of a variable. – KamilCuk Jun 11 '20 at 06:47
3

As @DimityKuzminov already pointed out, shared_from_this() will always return a std::shared_pointer<C> also when called from std::shared_ptr<D>. In order to use std::dynmaic_pointer_cast your base also needs to have a virtual function like a virtual destructor (otherwise you compiler will tell you source type is not polymorphic). Here is an example:

// enable_shared_from_this example
#include <iostream>
#include <memory>

struct C : std::enable_shared_from_this<C> {
    virtual ~C() = default;
};

template<typename T, typename U>
std::shared_ptr<T> 
shared_from(const std::shared_ptr<U> &a)
{
    return std::dynamic_pointer_cast<T>(a->shared_from_this());
}

struct D : public C {};

int main () {
  std::shared_ptr<D> foo, bar;

  foo = std::make_shared<D>();

  bar = shared_from<D>(foo);

  return 0;
}
StefanKssmr
  • 1,191
  • 7
  • 16
  • 1
    Thank you, good comment. However any virtual function is enough, there may be no virtual destructor. – Dmitry Kuzminov Jun 11 '20 at 06:48
  • Yes, you're right. I'll make an edit. – StefanKssmr Jun 11 '20 at 06:50
  • @DmitryKuzminov & StefanKssmr I wonder why there may be no virtual destructor? I did see it many times. Where am I wrong? – John Jun 11 '20 at 07:02
  • 1
    @John, you may define a virtual destructor, regular non-virtual destructor or have a default destructor: every option would work with smart pointers if constructed properly. Virtual destructor is needed if you don't use smart pointers or construct the shared_ptr with type erasure. But to use dynamic type casting you need *any* virtual function in the base class. – Dmitry Kuzminov Jun 11 '20 at 07:09
  • @John Indeed, you see them quite often, but there is no need to have a virtual destructor in order to derive from a class. This [answer](https://stackoverflow.com/a/461224/8359552) might be interesting for you. – StefanKssmr Jun 11 '20 at 07:14