Il tentativo fatto con setSubject in associazione a dynamic_cast, era per avere un metodo da reimplementare in base alle necessita' nelle classi concrete di observer, senza avere un sistema adatto al singolo caso specifico, spero di essermi spiegato questa volta.
Si, ma se ci pensi bene la chiave di tutto è la funzione update(): è li che devi discriminare i vari tipi di subject che interessano all'observer, non da altre parti. Il resto è solo nebbia in val padana.
Fermo restando l'ultimo codice che ti ho mostrato, è sufficiente modificare la update() in questo modo:
codice:
class DerObserverMoreSubject : public Observer {
public:

    void update(Subject1* subject) override {
        auto sub = mSubjects.find(subject);
        // se il subject non è nel set non faccio niente.
        
        if (sub != m_Subjects.end()) { 
            // i subjects interessati sono quelli che ho deciso di osservare
            auto ptr1 = dynamic_cast<DerSubject1*>(subject);
            if (ptr1 != nullptr) {
                // aggiorno l'observer con i dati di ptr2
            }

            auto ptr2 = dynamic_cast<DerSubject2*>(subject);
            if (ptr2 != nullptr) {
                // aggiorno l'observer con i dati di ptr2
            }

            auto ptr3 = dynamic_cast<DerSubject3*>(subject);
            if (ptr3 != nullptr) {
                // aggiorno l'observer con i dati di ptr3
            }

            // altri subjects non previsti sono ignorati.

        }
    }

};
Ovviamente ogni observer avrà la sua update() con i suoi subjects da discriminare e i subjects la loro notify() per la loro lista di observers. E il tutto funziona con N observers e M subjects. Qui ho usato dynamic_cast<>, che comunque non è così lento da farti prendere sonno , perché è più comodo, meno laborioso e più sicuro di enum più funzione virtuale.
Interessante std::set: tra l'altro toglie il pensiero di controllare l'unicita' del dato passato, qualora questo sia un requisito.
Oddio, se ci fosse un doppione dello stesso subject avresti due chiamate a update() di cui solo una ti serve per l'aggiornamento dell'observer, l'altra lo sovrascrive con gli stessi dati.