#include <iostream>
#include <typeinfo>

template<typename T> struct notify_tag {};

template<typename T> void notify(T*t);

template< typename T>
struct dispatcher
{
  static void dispatch(...)
  {
    std::cout<<"T("<<typeid(T).name()<<") has no or ambiguous notify_tag<U> base"<<std::endl;
  }

  // If you comment this one out, C4 is equivalent to C3 (see below)
  static void dispatch(T*t,notify_tag<T>*)
  {
    std::cout<<"T("<<typeid(T).name()<<") has notify_tag<T> base; ";t->notify();
  }

  template<typename U>
  static void dispatch(T*t,notify_tag<U>*)
  {
    std::cout<<"T("<<typeid(T).name()<<") has notify_tag<U("<<typeid(U).name()<<")> base with T!=U; ";t->U::notify();
  }
};

template<typename T>
void notify(T*t)
{
  dispatcher<T>::dispatch(t,t);
}

struct enable_shared_from_this
  : notify_tag<enable_shared_from_this>
{
  void notify()
  {
    std::cout<<"enable_shared_from_this"<<std::endl;
  }
};

struct shared_ptr_debugger
  : notify_tag<shared_ptr_debugger>
{
  void notify()
  {
    std::cout<<"shared_ptr_debugger"<<std::endl;
  }
};

struct C0 {};

struct C1 : enable_shared_from_this {};

struct C2 : shared_ptr_debugger {};

// This has multiple base classes of type notify_tag<?>,
// so it doesn't work automatically.
struct C3 : enable_shared_from_this, shared_ptr_debugger {};

struct C4 : enable_shared_from_this, shared_ptr_debugger, notify_tag<C4>
{
  void notify()
  {
    std::cout<<"C4 now delegating to its base classes"<<std::endl;
    ::notify(static_cast<enable_shared_from_this*>(this));
    ::notify(static_cast<shared_ptr_debugger*>(this));
  }
};

int main()
{
  C0 c0;
  C1 c1;
  C2 c2;
  C3 c3;
  C4 c4;

  notify(&c0);
  notify(&c1);
  notify(&c2);
  notify(&c3);
  notify(&c4);
}
