8

I want to write a function that can take either of STL generic list, deque or vector and search for a key in it. What would be method signature of this function and how can we implement it?

What I know is that if we are accepting any of the derived classes in function argument we can use base abstract class assuming all relevant derived ones have the functions you need for your question.

EDIT: We cannot pass container's iterators in the function argument. If we can that is easy. It has to be a container.

I was thinking: Assuming 'Container' is an abstract base class from STL containers (which its not, according to first answer below).

template bool Search(std::Container C, T& key);

Thanks

kometen
  • 6,536
  • 6
  • 41
  • 51
shaffooo
  • 1,478
  • 23
  • 28
  • 4
    They have no base class. Look at `` functions to see what they do. – chris Feb 22 '16 at 20:31
  • Thanks but your comment doesn't help with question asked. Most of pre-defined functions are helpful when provided a range but I need to pass in an STL container. – shaffooo Feb 22 '16 at 20:41
  • 5
    *Thanks but your comment doesn't help with question asked* -- Yes it does. It is stating that there is no base class for the STL containers. So your question is based on a false assumption. So forget about base classes and think how to do this another way. – PaulMcKenzie Feb 22 '16 at 21:01

3 Answers3

13

As SergeyA mentioned in his answer, C++'s STL does not have polymorphic containers (opposing to Java or C# interfaces).

Regarding your requested function signature, look into STL <algorithm> header. There are lot of functions operating on some data, using two pointers (iterators) to the beginning and end of data block. For example,

template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );

searching for some value in [first, last).

If you really want to pass whole container to the function, you'll similarly write

template<class Container, class T>
bool Search(const Container& container, const T& value)
{
    for (auto iterator = container.begin(); iterator != container.end(); ++iterator)
    {
        if (*iterator == value)
            return true;
    }
    return false;
}
Zereges
  • 5,139
  • 1
  • 25
  • 49
  • This is what I wanted. How can I get iterators from this template container? – shaffooo Feb 22 '16 at 21:19
  • @shaffooo I added pseudo implementation of such method. – Zereges Feb 22 '16 at 21:21
  • Great! 'auto' to the rescue. Thanks – shaffooo Feb 22 '16 at 21:22
  • I think 'return false;' should be outside the for loop. – shaffooo Feb 22 '16 at 21:23
  • 1
    If the container is of type `T` and your function is designed to work on the type that's in the container, then you don't need two parameters in the template definition: `template bool Search(const Container& container, const Container::value_type& value)` – PaulMcKenzie Feb 22 '16 at 21:26
  • 1
    @shaffooo, If you ever can't use `auto`, it's still possible through the `typename Container::[const_]iterator` convention. – chris Feb 22 '16 at 21:26
  • @chris or even `decltype(container.begin()) iterator` – Zereges Feb 22 '16 at 21:28
  • 1
    @PaulMcKenzie, Unfortunately, this can have some undesired consequences. For example, you have a container of `std::string` and you want to find `"abc"` (except long enough that SSO doesn't apply). Now you need to create a new string with an allocation and all. Such problems aren't usually a worry unless you're making a library where users might care, however. One advantage to your approach is that you can use braces as an argument to construct the value. – chris Feb 22 '16 at 21:28
  • 1
    @chris Then you can have default parameters for function templates. To illustrate: http://ideone.com/ZmMc0H Defaults to `value::type`, otherwise uses the provided `T` type. Of course I didn't even need to specify the first template argument -- it's to illustrate that if 1 arg is provided, the default does take effect. – PaulMcKenzie Feb 22 '16 at 21:40
5

Luckily for us, there is no base class for standard library containers. The only place I know of where polymorphic inheritance is used in standard library is streams, and this is what earned them such a bad fame.

Standard containers are non-polymorphic, thus fast. You will have to make your function template to work with any container.

For example,

template <class CONTAINER> bool exists(const CONTAINER& ctr, const typename CONTAINER::value_type& key); 
SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • I understand that we have to make template but what I want to know is what would be function signature. what will we pass for container, key can be of some generic type T defined as template. – shaffooo Feb 22 '16 at 20:40
  • @shaffooo, think iterators. – SergeyA Feb 22 '16 at 20:41
  • I think you are saying that we have an STL container in a caller function that we can get beginning and ending iterator and pass in both iterators and the key and search in that range or use 'find' function from . I know that solution. This question was asked in an interview and restriction is that I have to pass in any STL container *itself* – shaffooo Feb 22 '16 at 20:46
  • so will the function argument be std::list or std::deque or std::vector? I can only pass in one container argument and one key argument something like search(std::container c, T key) – shaffooo Feb 22 '16 at 20:56
3

Containers do not have base classes. They do not define an interface based on dynamic polymorphism. They define an interface based on static polymorphism. That is, they do implement certain methods in common, but they are not inherited from some prototype.

Therefore, you must use the standard C++ mechanism for static polymorphism: templates. The container itself must be a template parameter:

template<typename Container, ...>
bool IsFound(const Container &c, ...);

Of course, this will not prevent any user from passing types which are not vector, deque, or list. They could pass anything that fulfills the implicit static requirements that your IsFound function imposes.

You could pass a set for example, and it would probably work, to some degree. But it wouldn't be nearly as fast as calling set::find with the type.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • That is helpful. Now how can we make this container accept template data type. Can we do template > for accepting containers of different data types, int or double etc. – shaffooo Feb 22 '16 at 21:06
  • For STL containers, you know the type by utilizing `Container::value_type`. – PaulMcKenzie Feb 22 '16 at 21:13