In particolare mi interessava sapere se il modo in cui individuo il tipo del subject passato col metodo setSubject e' corretto (uso del dynamic_cast) o si puo' fare altrimenti, usando ad esempio typeid, ma da quanto letto in giro mi pare sconsigliato.
Si, perché il dynamic_cast<> è abbastanza lento rispetto a una chiamata virtuale, per cui si può "emulare" tramite una enum, una funzione virtuale e uno static_cast<>. Ma non entro nei dettagli perché non è l'oggetto del thread.
Qui mi perdo io: in che senso parlo di piu' observer?
Io ho solo riportato quanto hai scritto. Immagino per estendere il pattern.
Se poi esistono altre strade percorribili, per le mie necessita', se non stravolgono la logica e non sono complicate, conoscere roba nuova puo far solamente bene, altrimenti, essendo programmi per uso personale, non sono alla disperata ricerca di prestazioni o che altro, una volta che funzionano, va bene.
La strada "accademica" è interessante ma lunga.
La cosa più semplice è mettere in overload la funzione update() affinché riceva il dato corretto.
Ti posto un esempio. Nel main puoi vedere come i metodi invocati sono solo quelli dell'interfaccia Observer e Subject.
Per quanto riguarda lo std::set<>, il suo scopo è solo quello di avere una singola istanza di quanto gli viene passato, cosa che con il vector (di inizio thread) diventa complicato.
codice:
class Subject;
// forward declaration necessarie per i metodi in overload
class DerSubject1;
class DerSubject2;
class DerSubject3;

class Observer {
    public:
        virtual ~Observer() {};
        void setSubject(Subject* sub);
        void removeSubject(Subject* sub);

        // update in overload per tipo di subject da ridefinire 
        // nelle derivate da Observer
        virtual void update(DerSubject1* subject) = 0;
        virtual void update(DerSubject2* subject) = 0;
        virtual void update(DerSubject3* subject) = 0;

protected:
        Observer() {};
        std::set<Subject*> mSubjects;
};

class Subject {
public:
    virtual ~Subject() {};
    void attach(Observer* observer) {
        mObserverList.insert(observer);
    }

    virtual void detach(Observer* observer) {
        mObserverList.erase(observer);
    }
    virtual void notify()= 0;
protected:
    Subject() {};
    std::set<Observer*> mObserverList;
};

// occorre metterle qui perché hanno bisogno della definizione delle 
// funzioni di Subject
void Observer::setSubject(Subject* sub) {
    sub->attach(this);
    mSubjects.insert(sub);
}

void Observer::removeSubject(Subject* sub) {
    sub->detach(this);
    mSubjects.insert(sub);
}

// la funzione notify dev'essere ridefinita nelle singole 
// derivate da Subject perché dev'essere propagato
// il tipo di dato reale.
class DerSubject1 : public Subject {
    public:
        // per l'interfaccia Subjects
        void notify() {
            for( auto it : mObserverList) {
                it->update(this);
            }
        }

        // funzione propria
        std::string getMyName1() { return "DerSubject1"; }
};

class DerSubject2 : public Subject {
public:
    // per l'interfaccia Subjects
    void notify() {
        for(auto it : mObserverList) {
            it->update(this);
        }
    }

    // funzione propria
    std::string getMyName2() { return "DerSubject2"; }
};

class DerSubject3 : public Subject {
public:
    // per l'interfaccia Subjects
    void notify() {
        for(auto it : mObserverList) {
            it->update(this);
        }
    }

    // funzione propria
    std::string getMyName3() { return "DerSubject3"; }
};

class DerObserverMoreSubject : public Observer {
public:

    void update(DerSubject1* subject) override {
        std::cout << "DerObserverMoreSubject observes " << subject->getMyName1() << std::endl;
    };

    void update(DerSubject2* subject) override {
        std::cout << "DerObserverMoreSubject observes " << subject->getMyName2() << std::endl;
    }

    void update(DerSubject3* subject) override {
        std::cout << "DerObserverMoreSubject observes " << subject->getMyName3() << std::endl;
    }
};


using namespace std;

int main(int argc, char* argv[]) {

    DerObserverMoreSubject doms;
    DerSubject1 ds1;
    DerSubject2 ds2;
    DerSubject3 ds3;

    Observer* ob = &doms;
    Subject* s1 = &ds1;
    Subject* s2 = &ds2;
    Subject* s3 = &ds3;

    ob->setSubject(s1);
    ob->setSubject(s2);
    ob->setSubject(s3);

    s1->notify();
    s2->notify();
    s3->notify();
}