Apart from the reasons posted here there is also another one - binary compability. Libraries' writers have no control over which
std::string implementation you are using and whether it has the same memory layout as theirs.
std::string is a template, so its implementation is taken from your local STL headers. Now imagine that you are locally using some performance-optimised STL version, fully compatible with the standard. For example, you may have chosen to intrudce static buffer in each
std::string to reduce the number of dynamic allocations and cache misses. As a result, memory layout and/or size of your implementation is different than library one's.
If only the layout is different, some
std::string member function calls on instances passed from library to the client or the other way around may fail, dependending on which members were shifted.
If the size is different as well, all library types having
std::string member will appear to have different sizeof when checked in the library and in the client code. Data members following
std::string member will have offsets shifted as well, and any direct access/inline accessor called from the client will return rubbish, despite "looking OK" when debugging the library itself.
Bottomline - if library and the client code are compiled agains different
std::string versions, they will link just fine, but it may result in some nasty, hard to understand bugs. If you change your
std::string implementation all libraries exposing members from STL have to be recompiled to match the client's
std::string layout. And because programmers want their libraries to be robust you'll rarely see
std::string exposed anywhere.
To be fair, this applies to all STL types. IIRC they don't have standarised memory layout.