Mach einfach einen std::vector<com_ptr<Whatever>>
Das würde ja auch nur funktionieren wenn der com_ptr aufjedenfall nur IUnknown Objekte/Pointer nimmt um dann im destruktor Release() aufzurufen oder nicht?
Nein, Templates funktionieren anders als Generics. Ein Template wird für jeden gegebenen Typ neu instantiiert, das heißt, für jeden gegebenen Typ erzeugt der Compiler eigenen Code, in dem ein Template-Parameter nicht durch eine gemeinsame Typschranke, sondern tatsächlich den vollständigen übergebenen Typ ersetzt wird. Instantiierst du nun einen com_ptr<IAdapter>, wie im anderen Thread, dann wird der Template-Parameter T in dieser Instanz nicht zu IUnknown, sondern tatsächlich zu IAdapter. Ein com_ptr<IDevice> würde dagegen anderen Code erzeugen, in dem der Template-Parameter T tatsächlich IDevice wäre. Da beide Typen von IUnknown erben, lässt sich trotzdem für beide im Destruktor Release() aufrufen, beide rufen aber grundsätzlich unterschiedliche com_ptr-Destruktoren auf, wenngleich das in diesem Fall keinen Unterschied macht. Würdest du com_ptr<Foo> instantiieren, ohne dass Foo eine Release()-Methode anbietet, dann würdest du für den com_ptr-Destruktor dieser Instantiierung natürlich einen Compiler-Fehler erhalten.
(Nebensächliche Anmerkung: Erzeugen verschiedene Template-Instantiierungen identischen Code, so verschmilzt der Compiler diesen bei der Optimierung wieder. Das hat keine Auswirkung auf das Programmverhalten, verhindert aber ein Explodieren der Programmgröße.)
Vektoren von com_ptr<IIrgendeinD3DInterface> kannst du auf jeden Fall problemlos anlegen, und musst dann auch keine Funktionen mit Schleifen zum Aufräumen mehr schreiben, weil bei Zerstörung des Vektors automatisch alle enthaltenen com_ptrs zerstört werden, und damit automatisch für alle enthaltenen COM-Zeiger Release() aufgerufen wird.