3

I am currently looking at STL libraries and am wondering why for a vector class vector<string> names; I have to call remove(); as follows:

names.erase(remove(names.begin(), names.end(), "Simon"), names.end());

While when using a list class list<string> names; I can call the function as follows:

remove("Simon");

I also notice the same for reverse(); for vector<string> names; it is called as follows:

reverse(names.begin(), names.end());

While for list<string> names; it is called as follows:

names.reverse();

Is it more proper to always call the way the vector would? why is this? I'm very new to C++ so I'm keen to know the best way to do things.

stuart194
  • 361
  • 1
  • 2
  • 10
  • See [what's the difference between list.sort and stl sort?](http://stackoverflow.com/questions/8017215/whats-the-difference-between-list-sort-and-stdsort) In brief: that happens when the algorithm can be implemented more efficiently for those classes when the algorithm has access to the internals of the class, than by working with the iterators it provides alone. The general syntax only grants access to the interface. Member function calls can use the internals. – jaggedSpire Dec 05 '16 at 22:40
  • 4
    *"I'm very new to C++"* That explains why you expect naming in C++ to make sense. ;) Two more fun bits: `std::remove_copy_if` does not remove anything and calling `std::toupper` on a `char` potentially invokes UB. Welcome to C++. :) – Baum mit Augen Dec 05 '16 at 22:42
  • @n.m. I know that, the remove is meant to be called inside the erase and I was wondering why. Even if I don't call from within an erase it still needs the different format. – stuart194 Dec 05 '16 at 22:44
  • @BaummitAugen calling `std::abs` on `int` can invoke UB too. As can simple act of adiition of two numbers. Have fun indeed. – Revolver_Ocelot Dec 05 '16 at 22:44
  • @BaummitAugen, in that case I'm going back to my electronic hardware! Nightmare! – stuart194 Dec 05 '16 at 22:47

1 Answers1

7

Basically, there are some special cases that have to do with the nature of specific containers.

In general the free functions std::remove, std::remove_if, and std::reverse declared in the <algorithm> header will work on vectors, lists, deques, and arrays by copying and moving elements. (They will not, of course, work on sets or maps, since for those you are not free to rearrange elements willy-nilly.) Note that std::remove does not delete elements from containers.

In general the member function erase of each container type is used to delete elements from that container. (Note that std::array does not have erase because its size is fixed.)

There are special cases:

  • std::list provides reverse, as a member because only the member function can guarantee that it does not invalidate any iterators; the generic std::reverse cannot.
  • The same goes for remove and remove_if though the names are misleading because unlike the free functions, the members do delete elements from the list.
  • There is also a member sort for std::list because the generic std::sort only works on random access iterators.
  • For sets and maps we should use their member lower_bound, upper_bound, equal_range, and count instead of the generic versions, since they know how to walk down the tree and get the result in logarithmic time whereas the free functions will use linear time.

Generally, the principle appears to be: the standard library containers support uniform interfaces as far as possible, but also provide additional specialized functions in order to provide functionality that depends on their internals.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • Thanks for shedding some light on this! So when I am using a list class, It is generally best to call them the as I have shown? – stuart194 Dec 05 '16 at 22:54
  • @stuart194 Yes, I think you should usually use the container-specific functions when applicable. – Brian Bi Dec 05 '16 at 23:01