C++ – Is it bad practice to use Inheritance to associate methods with a basic container

c

Basically, I have code that looks like this. It's a typedef and a set of methods that revolve around that typedef.

class foo {
    // Foo stuff...

    // Registration Stuff
    private:
        typedef std::map<std::string, std::string> MTRegistrationParams;

        void DispatchRegistrationMessage (MTRegistrationParams const &, std::string destination_id);
        static std::string BuildRegistrationMessage (MTRegistrationParams const &, std::string message_id); // Only called by DispatchRegistrationMessage

    // more Foo stuff...
};

I am thinking it might be clearer to just make a little mini-class, to help logically separate those methods from Foo's structure.

class foo {
    // Foo stuff...

    // Registration Stuff
    private:
        struct MTRegistrationParams : public std::map<std::string, std::string> {
            void DispatchRegistrationmessage (foo &, std::string const & destination_id);
            private:
                std::string BuildRegistrationMessage (std::string const & message_id);
    };

    // more Foo stuff...
};

The thing is… I don't usually make derived classes unless for the sake of inheriting methods, and deriving from a standard container seems REALLY weird.

Am I being overly concerned, or is deriving from standard containers just to decorate them with methods genuinely bad practice?

Best Answer

Deriving from a C++ standard container should look really weird, because they were never designed to be used as a base class.

The biggest source of problems is if your 'extended' container needs to override behavior of the underlying container (including the destructor!), or if your 'extended' container has additional constraints that don't exist for the underlying standard container.
When passing such an 'extended' container as a pointer/reference to a function that expects a standard container, that function can inadvertently break the invariants of the 'extended' container by calling the wrong functions of the base class. And if you happen to destruct an 'extended' container through a pointer to its base class, you land yourself in undefined behavior.

In the example of this question, the chances for such incorrect behavior are next to nil, but if you start using this pattern, it is easy to land in situations where it does matter. For that reason, it is strongly recommended not to derive from the standard containers.

Also, the DispatchRegistrationMessage method isn't that much easier to use, because now you need to pass in a Foo object and it is very inconvenient to use overloaded operators (like operator[] of std::map) on this.