Simple wrapper for member function pointers with known signature
Sorry for the brief answer, typing on phone.

Your NetworkPublisher should have Register(int signal, std::function&lt;void()&gt; callback)

And then simply do:

publisher.Register(WHATEVER_SIGNAL, std::bind(&amp;MyClass::MySignalCallback, this));


This way you dont need the PacketCallback class and your subscriber interface is more generally usable as you can now pass lamdas and free functions as well. Further more your NotifySubscribers will simplify a bit.

Side note: You might want to consider using smart pointers instead of raw pointers. Or references if null is not an allowed argument.

Edit: In reply to comments, this is one way I would consider of doing it:

enum class SignalType{
   NewConnection,
   ConnectionLost
};

const std::array&lt;SignalType,2&gt; AllSignalTypes = {
    SignalType::NewConnection,
    SignalType::ConnectionLost};

class ISubscriber{
public:
    virtual bool supportsSignal(SignalType signalType) const = 0;
    virtual void signal(SignalType signalType, const RakNet::Packet&amp; packet) = 0;
}

class Publisher{
public:
    void registerSubscriber(const std::shared_ptr&lt;ISubscriber&gt;&amp; subscriber){
        for(auto&amp; type : AllSignalTypes){
            if(subscriber-&gt;supportsSignal(type)){
                // TODO: Check for duplicates or change to std::unordered_set&lt;weak_ptr&gt;
                subscribers[type].emplace_back(subscriber);
            }
        }
    }

    void signal(SignalType signalType, const RakNet::Packet&amp; packet){
        forEachLiveSubscriber(signalType, [&amp;packet](const std::shared_ptr&lt;ISubscriber&gt;&amp; subscriber){
            subscriber-&gt;signal(signalType, packet);
            return true;
        });
    }

    void unregisterAll(const ISubscriber&amp; subscriber){
        // Technically not necessary but for good measure
        for(auto&amp; type : AllSignalTypes){
            forEachLiveSubscriber(signalType, [&amp;packet](const std::shared_ptr&lt;ISubscriber&gt;&amp; sharedSubscriber){
                return sharedSubscriber.get() != &amp;subscriber;
            });
        }
    }

private:
    std::unordered_map&lt;SignalType, std::vector&lt;std::weak_ptr&lt;ISubscriber&gt;&gt;&gt; subscribers;

    template&lt;typename Callable&gt;
    void forEachLiveSubscriber(SignalType signalType, Callable&amp;&amp; callback){
        auto&amp; signalSubscribers = subscribers[signalType];

        auto it = signalSubscribers.begin();
        while(it != signalSubscribers.end()){
            auto&amp; strongPtr = it-&gt;lock();
            if(strongPtr &amp;&amp; callback(strongPtr)){
                it++;
            }
            else{
                // The subscriber has destructed without unregistering,
                // Or the callback requested us to remove it.
                it = signalSubscribers.erase(it);
            }
        }
    }
}

class MySubscriber : public ISubscriber{
    bool supportsSignal(SignalType signalType) const override{
        return signalType == NewConnection || signalType == ConnectionLost;
    }

    void signal(SignalType signalType, const RakNet::Packet&amp; packet) override{
        switch(signalType){
            case NewConnection:
                std::cout&lt;&lt;"Someone connected"&lt;&lt;std::endl;
                break;
            case ConnectionLost:
                std::cout&lt;&lt;"Someone lost connection"&lt;&lt;std::endl;
                break;
        }
    }
}


Note that by using shared_ptr we get access to weak_ptr which in turn allows lazy garbage collection of the subscribers. In turn this means that a subscriber doesn't have to unregister if all shared pointers to it just simply die. 

The above code is untested pseudocode. Some assembly may be required :)