Search code examples
c++stlc++11overloadingstl-algorithm

STL algorithms: Why no additional interface for containers (additional to iterator pairs)?


I'm wondering why the STL doesn't overload their algorithm functions such that I can call them by simply providing a container and not taking the more verbose way to pass begin + end iterators. I of course understand why we also want to use an iterator pair for processing subsequences of a container / array, however, almost all calls to these methods are using a whole container:

std::for_each(myVector.begin(), myVector.end(), doSomething);

I'd find it more convenient, readable and maintainable to just write

std::for_each(myVector, doSomething);

Is there a reason STL doesn't provide these overloads? [EDIT: I don't mean to replace the interface with this restricted one but to also provide a container-based iterface!] Do they introduce ambiguity? I'm thinking about something like this:

template<typename _Container, typename _Funct>
inline _Funct for_each(_Container c, _Funct f) {
    return for_each(begin(c), end(c), f);
}

Am I missing something?


Solution

  • They do introduce ambiguity for many algorithms. A lot of <algorithm> looks like

    template<class iterator>
    void do_something(iterator, iterator);
    
    template<class iterator, class funct>
    void do_something(iterator, iterator, funct);
    

    If you add additional overloads

    template<class container, class funct>
    void do_something(container, funct);
    

    the compiler will have some trouble figuring out what do_something(x, y) means. If x and y are of the same type, it will match both iterator = type and container = type, funct = type.*)

    C++11 tried to solve this with "concepts" that could recognize the difference between a container and an iterator. However, these "concepts" turned out to be too complicated to make it into the standard, so neither did these overloads.

    *) compilers disagree here, the Comeau compiler claims that it is ambiguous, g++ 4.5 and MSVC 10 calls the first function.


    After an extremely long discussion in the comments, here is one example where it doesn't work as expected - using a container adapter that can also double as a predicate.

    #include <iostream>
    #include <vector>
    
    template<class iterator>
    void test(iterator, iterator)
    {
       std::cout << "test iterator\n";
    }
    
    template<class iterator, class predicate>
    void test(iterator, iterator, predicate)
    {
       std::cout << "test iterator, predicate\n";
    }
    
    template<class container, class predicate>
    void test(const container& cont, predicate compare)
    {
       std::cout << "test container, predicate\n";
    
       test(cont.begin(), cont.end(), compare);
    }
    
    template<class container>
    class adapter
    {
    public:
       typedef typename container::iterator   iterator;
    
       adapter(container* cont) : cont(cont)
       { }
    
       iterator begin() const
       { return cont->begin(); }
    
       iterator end() const
       { return cont->end(); }
    
       bool operator()(const iterator& one, const iterator& two)
       { return *one < *two; }
    
    private:
       container* cont;
    };
    
    int main()
    {
       std::vector<int>   v;
    
       adapter<std::vector<int>>   a(&v);
    
       test(a, a);
    
    }
    

    Output:

    test iterator

    http://ideone.com/wps2tZ