Le prestazioni hanno due parti: 
-  Fai meno lavoro possibile. 
 
-  Che lavoro fai, fai il più rapidamente possibile. 
 
 Qui la seconda parte può essere indirizzata abbastanza rapidamente: non utilizzare   dynamic_cast   , vedremo modi per evitarlo in un minuto. Non costruire lo stesso oggetto più e più volte. Per lo meno, vogliamo qualcosa di simile: 
template <class TYPE>
A* search(TYPE const& expected) {
  static_assert( TYPE is subclass of A );
  for (TYPE* obj : magically_get_eligible_objects<TYPE>()) {
    if (obj && (*obj == expected))
      return obj;
  }
  return nullptr;
}
 In C ++, i modelli e il polimorfismo non si adattano affatto bene. Non ci sarà una soluzione elegante e sicura per il tipo a   magically_get_eligible_objects()   . Ma se scriviamo il codice corretto possiamo prendere scorciatoie e violare il sistema di tipo C ++ in qualche modo in sicurezza. Per il resto di questa risposta, solo i TIPI di foglie sono rilevanti. Anche il tipo   A*    potrebbe essere   void*   . 
 Poiché conosci sempre il TIPO per una query, puoi suddividere gli oggetti per tipo. Non è necessario memorizzarli in una singola struttura dati. Ciò contribuirà a ridurre lo spazio di ricerca se la popolazione di oggetti è ben distribuita su più tipi: se al massimo il 30% degli oggetti ha lo stesso tipo, anche il tempo peggiore per una ricerca è sceso al 30% (più sovraccarico). 
 Per questo partizionamento puoi usare   map<type_index, vector<A*>>    o   multimap<type_index, A*>    nella condizione in cui usi   type_index(typeid(obj))    di un oggetto come chiave per inserire, eliminare e cercare oggetti o intervalli di oggetti. Quando si ottengono iteratori o raccolte su un numero di oggetti che sono nominalmente di tipo A ma si conosce il loro TIPO effettivo, allora è possibile * tossire *   reinterpret_cast    delle raccolte. Dal momento che stai memorizzando solo i puntatori, questo dovrebbe essere sicuro nella pratica. Per i singoli puntatori puoi utilizzare   static_cast    in sicurezza. 
 Nota: se tutti gli oggetti hanno una costante   name    o altri dati che sono conosciuti da  tutte  ricerche, allora la chiave può essere un   tuple<type_index, NameType>    invece che dovrebbe portare ad un partizionamento sufficientemente buono . Continuerò a pensare che non sia così. 
 Probabilmente, potresti usare una struttura dati   tuple<vector<TYPE1*>, vector<TYPE2*>, ..., vector<TYPEn*>>    invece e cercare le partizioni per tipo. Questo evita alcuni cast ma è (a parte quella sicurezza di tipo e potenzialmente un po 'di performance di runtime) non fondamentalmente superiore a   map<type_index, vector<void*>>   . Questo rende le operazioni polimorfiche (come l'inserimento di un elemento in cui il TYPE non è noto) molto più difficile. 
 All'interno di una partizione, gli oggetti sarebbero ancora non ordinati, richiedendo la scansione lineare. Se i TYPE specifici hanno proprietà più forti, avremmo bisogno di introdurre un tipo di partizione personalizzato che possa essere specializzato per i singoli TIPI. 
 Avremmo bisogno di una partizione di base astratta per la compilazione del contenitore della partizione, qualcosa del tipo: 
class BasePartition {
public:
  virtual void insert(void*) = 0;
  virtual void* search_by_name(NameType const&) = 0;
  virtual void* search_by_expected(void*) = 0;
};
 Quindi una classe template che fa automaticamente riferimento a un vettore non isolato, ma potrebbe essere specializzata per strutture dati migliori: 
template<class TYPE>
class Partition : BasePartition {
  vector<TYPE*> objects;
public:
  void insert(void* obj) override {
    objects.push_back(static_cast<TYPE*>(obj));
  }
  void* search_by_name(NameType const& name) override { ... }
  void* search_by_expected(void* untyped_expected) override {
    // may prefer dynamic_cast for more defensive code
    TYPE* untyped_expected = static_cast<TYPE*>(untyped_expected);
    ...
  }
};
 Potremmo quindi avere un contenitore   map<type_index, unique_ptr<BasePartition>> partitions   . Dato un   BasePartition* partition    per il TYPE corretto, la funzione   search()    potrebbe quindi essere semplificata in 
// partition casts internally
return static_cast<TYPE*>(partition->search_by_expected(&expected));
 Possiamo quindi specializzare il modello   Partition    per i tipi in cui è possibile una ricerca più efficiente. Per esempio. se è possibile ordinare un tipo, potremmo utilizzare   set    anziché   vector   . Puoi anche conservare   unordered_map    s aggiuntivo per indicizzare gli oggetti con alcune proprietà, ad es. per nome. Se l'utilizzo della memoria aggiuntiva è un compromesso valido dipende dalla tua applicazione.