#include <iostream>
#include <unordered_set>
#include <memory>
#include <vector>
#include <deque>

template < typename T, typename TAG = void  > struct magic
{
    // ...
    /* 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 { /* ... */ };

template < int N > struct use_case {} ;

int main()
{
   // the general solution is not imposed; nothing needs to be done to
   // 'let the specific use-cases choose how to store the objects'
   std::vector< std::shared_ptr<A> > seq1(5) ;
   for( int i = 0 ; i < 5 ; ++i ) seq1.emplace_back( new A ) ;

   // the general solution can be used in conjunction with aspecial case solution
   // here, objects participate multiple use cases
   // all objects participate in the this use case, half of them also participate
   // in the first use case, and the other half also participates in
   // a new use-case using the general solution.
   std::deque< std::shared_ptr<A> > seq2 { seq1.begin(), seq1.end() } ;
   for( int i = 0 ; i < 5 ; ++i ) seq2.emplace_front( new tracked< A, use_case<1> > ) ;

   // here, the same set of objects participate in two different use cases
   // with both use cases implemented using just the general solution
   tracked< A, use_case<2> > three[5] ;
   std::vector< std::reference_wrapper<A> > four ;
   for( A a : three )
       four.emplace_back( tracked< std::reference_wrapper<A>, use_case<3> >(a) ) ;
}
