You're still not letting the specific use-cases choose how to store the objects, which is what the default would be without the global static variable.
Still though, I am curious how you plan to implement objects being in two or more use-cases - that is, an overlap.
EDIT: I guess the disconnect is made more obvious when I say, I don't understand why you're using a set when some use-cases could want duplicates.
> You're still not letting the specific use-cases choose how to store the objects
It doesn't prevent anything. A specific use-case can decide how to store the objects. Use of the default is an option; whether to use it or not is up to the user.
> I am curious how you plan to implement objects being in two or more use-cases - that is, an overlap.
#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) ) ;
}