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_string
1 (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
.