In addition to the other discussion here, it may be worth noting that you can have global-ness, without limiting usage to one instance. For example, consider the case of reference counting something...
struct Store{ std::array<Something, 1024> data; size_t get(size_t idx){ /* ... */ } void incr_ref(size_t idx){ /* ... */} void decr_ref(size_t idx){ /* ... */}};template<Store* store_p>struct ItemRef{ size_t idx; auto get(){ return store_p->get(idx); }; ItemRef() { store_p->incr_ref(idx); }; ~ItemRef() { store_p->decr_ref(idx); };};Store store1_g;Store store2_g; // we don't restrict the number of global Store instances
Now somewhere inside a function (such as main
) you can do:
auto ref1_a = ItemRef<&store1_g>(101);auto ref2_a = ItemRef<&store2_g>(201);
The refs don't need to store a pointer back to their respective Store
because that information is supplied at compile-time. You also don't have to worry about the Store
's lifetime because the compiler requires that it is global. If there is indeed only one instance of Store
then there's no overhead in this approach; with more than one instance it's up to the compiler to be clever about code generation. If necessary, the ItemRef
class can even be made a friend
of Store
(you can have templated friends!).
If Store
itself is a templated class then things get messier, but it is still possible to use this method, perhaps by implementing a helper class with the following signature:
template <typename Store_t, Store_t* store_p>struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning instances of ItemRef<Store_t, store_p>. */ };
The user can now create a StoreWrapper
type (and global instance) for each global Store
instance, and always access the stores via their wrapper instance (thus forgetting about the gory details of the template parameters needed for using Store
).