Alex
Alex

Reputation: 846

How does shared_ptr<T> detect that T derives from enable_shared_from_this<T>?

I am trying to learn how shared_ptr works by implementing it from scratch, and I can't figure out how to detect T's base class.

I've tried using is_base_of(), but that gives a const value, which I can't use with an if statement to set the object's internal weak_ptr.

I was thinking along these lines:

template <class T>
class shared_ptr
{
    shared_ptr(T* ptr)
    {
        ...
    }

    shared_ptr(enable_shared_from_this<T>* ptr)
    {
        ...

        Ptr->m_this = weak_ptr<T>(this);
    }
};

but no luck so far. Boost's and VC++ implementations are too confusing for me, I'm looking for a simple explanation.

Here it says

The constructors of std::shared_ptr detect the presence of an enable_shared_from_this base and assign the newly created std::shared_ptr to the internally stored weak reference.

Yeah, how?

Upvotes: 3

Views: 413

Answers (2)

Pradhan
Pradhan

Reputation: 16737

Simple - use template argument deduction! That's the solution to all the world's problems, but you knew that already :) A solution based on the way boost solves your problem is below. We create a templated helper class which actually handles the details of the construction.

template <class T>
class shared_ptr
{
    shared_ptr(T* ptr)
    {
        magic_construct(this, ptr, ptr);
    }
};

template <class X, class Y, class Z>
void magic_construct(shared_ptr<X>* sp, Y* rp, enable_shared_from_this<Z>* shareable)
{
//Do the weak_ptr handling here
}

void magic_construct(...)//This is the default case
{
//This is the case where you have no inheritance from enable_shared_from_this
}

Upvotes: 1

Grigorii Chudnov
Grigorii Chudnov

Reputation: 3112

One option is to make it based on function template overloading.

Here is a simplified solution: We have two classes A and B. Class A derives from H. Function is_derived_from_h is overloaded and can be used to detect whether some class X is derived from H.

#include <stdlib.h>
#include <iostream>

class H {};
class A: public H {};
class B {};

// (1)
template <typename X>
void is_derived_from_h(X* px, H* ph) {
  std::cout << "TRUE" << std::endl;
}

// (2)
void is_derived_from_h(...) {
  std::cout << "FALSE" << std::endl;
}

int main(int argc, char* argv[]) {

  A* pa = new A;
  B* pb = new B;

  is_derived_from_h(pa, pa); // (1) is selected, the closest overload
  is_derived_from_h(pb, pb); // (2) is selected, (1) is not viable

  delete pa;
  delete pb;

  return EXIT_SUCCESS;
}

Output:

TRUE
FALSE

In case of Boost, trace the following calls:

shared_ptr( Y * p )
->
boost::detail::sp_pointer_construct( this, p, pn );
  ->
boost::detail::sp_enable_shared_from_this( ppx, p, p );

Threre are several versions of sp_enable_shared_from_this. The version that is selected based on whether Y derives from enable_shared_from_this or not.

Upvotes: 1

Related Questions