A simple guess:
The different methods you cite have different behavior, which is probably why some use iterators and other don't.
std::for_each is generic - The easiest way to have a generic version of a method that works on any containers (and even on raw array) is to use iterators.
std::vector::erase is part of the SequenceContainer concept, so it must have a "generic" form that can work on any kind of container (you could use pos and count for a std::vector, but what about an std::list? Or a std::set?). Having such concept is useful to create generic code:
template <typename C>
void real_remove(C &c, const typename C::value_type &value) {
c.erase(std::remove(c.begin(), c.end(), value), c.end());
}
This only works because std::...::erase is well-defined for any SequenceContainer.
On the other hand, std::basic_string::substr is only part of std::basic_string (unlike erase which is part of std::vector, std::list, ...) and returns a std::basic_string1 (not an iterator, what would you do with an iterator here?).
There are other "non-generic" (i.e. that are not mandatory by some concepts) methods in std::basic_string, typically the whole family of find methods, insert has an overload with size_type, append, and so on.
On a subjective view, I think it is better to have a std::basic_string that does not behave like other containers because it is not (I am not sure that the standard requires std::basic_string to be a SequenceContainer or anything alike).
1 You cannot return iterators here because you want a new std::basic_string, so you would have a method taking iterators but returning an object... I would find this much more disturbing than having pos/count instead of first/last.