В дополнение к другому обсуждению здесь, возможно, стоит отметить, что вы можете иметь глобальность, не ограничивая использование одним экземпляром. Например, рассмотрим случай подсчета ссылок ...
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
Теперь где-то внутри функции (например, main
) вы можете сделать:
auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201);
Ссылкам не нужно хранить указатель обратно на их Store
, потому что эта информация предоставляется во время компиляции. Вам также не нужно беспокоиться о времени жизни Store
, потому что компилятор требует, чтобы он был глобальным. Если в действительности существует только один экземпляр Store
, то в этом подходе нет накладных расходов; с более чем одним экземпляром компилятор должен быть умным в отношении генерации кода. При необходимости класс ItemRef
можно даже сделать friend
из Store
(вы можете иметь шаблонных друзей!).
Если Store
сам по себе является шаблонным классом, то ситуация становится более запутанной, но все еще возможно использовать этот метод, возможно, путем реализации вспомогательного класса со следующей сигнатурой:
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>. */ };
Теперь пользователь может создать тип (и глобальный экземпляр) StoreWrapper
для каждого глобального экземпляра Store
и всегда получать доступ к хранилищам через свой экземпляр-обертку (таким образом, забывая о мрачных деталях параметров шаблона, необходимых для использования Store
).