#include <iostream>
#include <unordered_set>
#include <memory>

template < typename T, typename TAG = void  > struct magic
{
    magic() { objects.insert( static_cast<T*>(this) ) ; }

    magic( const magic& that ) : magic() {}

    magic( magic&& that ) : magic() {}

    ~magic() { objects.erase( static_cast<T*>(this) ) ; }

    template< typename FN > static void for_each_instance( FN fn )
    { for( T* p : objects ) fn(p) ; }

    private: static std::unordered_set<T*> objects ;
};

template < typename T, typename TAG > std::unordered_set<T*> magic<T,TAG>::objects ;

template < typename T, typename TAG = void > struct tracked
                                              : T, magic< tracked<T,TAG>, TAG >
{
    template < typename ... ARGS > tracked( ARGS... args ) : T(args...) {}
};

struct A
{
    virtual ~A() {}
    void foo( int i, char c )
    { std::cout << "A::foo( " << this << ", " << i << ", " << c << " )\n" ; }
};

struct B
{
    B( int a = 0, double b = 0 ) {}
    virtual ~B() {}
    void bar( const char* cstr )
    { std::cout << "B::bar( " << this << ", " << cstr << " )\n" ; }
};

int main()
{
    A not_tracked[10] ;
    B also_not_tracked[20] ;

    std::unique_ptr<A> default_tracked( new tracked<A> ) ;
    std::unique_ptr<B> also_default_tracked( new tracked<B> ) ;

    struct my_objects {};
    A* my_tracked_A_objects = new tracked< A, my_objects >[2] ;
    tracked< B, my_objects > my_tracked_B_objects[3] { {1,2.3}, {4,5.6}, {7,8.9} } ;

    struct their_objects {};
    tracked< A, their_objects > their_tracked_A_objects[4] ;

    std::cout << "tracked A (default)\n-----------------------\n" ;
    tracked<A>::for_each_instance( []( A* pa ) { pa->foo( 1, 'x' ) ; } ) ;

    std::cout << "\ntracked A (my_objects)\n---------------------\n" ;
    tracked<A,my_objects>::for_each_instance( []( A* pa ) { pa->foo( 2, 'y' ) ; } ) ;

    std::cout << "\ntracked A (their_objects)\n--------------\n" ;
    tracked<A,their_objects>::for_each_instance( []( A* pa ) { pa->foo( 3, 'z' ) ; } ) ;

    std::cout << "\ntracked B (my_objects)\n--------------\n" ;
    tracked<B,my_objects>::for_each_instance( []( B* pb ) { pb->bar( "hello" ) ; } ) ;
}
