Search code examples
c++templatesstlsetfunction-object

STL and std custom compare arguments working in c++


I asked this question on stackoverflow STL passing object

I got to know that we pass objects which in tern call the compare operator in them and compare our values and gives us a result. All good.

Now in this piece of code:

class Compare {
    public:
    bool operator ()(const int &a, const int &b) { return a < b;}
};

void solve(){
    set<int, Compare> s;
    vector<int> arr = {1,4,6,3,7,2,8,588,5};    
    for(int e : arr) s.insert(e);
    for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl;
}

I get error if I do Compare() which is what I thought must be done(passing an object). Why so? How can we pass Compare?

Also in this piece of code I cannot pass a function like:

bool comp(const int &a, const int &b) { return a < b; }

void solve(){
    set<int, comp> s;
    vector<int> arr = {1,4,6,3,7,2,8,588,5};    
    for(int e : arr) s.insert(e);
    for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl;
}

Which is what I expect. Not passing functions.

Now in this piece of code:

bool comp(const int &a, const int &b) { return a < b; }

void solve(){
    vector<int> arr = {1,4,6,3,7,2,8,588,5};    
    nth_element(arr.begin(), arr.begin()+3, arr.end(), comp);
    for(int e : arr) cout << e << " "; cout << endl;
}

I am able to pass a function and there is no error. I mean what is going on? From my perspective, we should just pass objects and they will call their () overloaded operator and compare values but sometimes we are passing functions, sometimes passing objects giving error and sometimes we just pass class name. What is exactly going on in c++


Solution

  • In the first code snippet the compiler issues an error because you was using an object Compare() as the type template argument instead of the type Compare.

    If you will write for example

    #include <iostream>
    #include <vector>
    #include <set>
    
    using namespace std;
    
    class Compare {
        public:
        bool operator ()(const int &a, const int &b) const { return a < b;}
    };
    
    int main() 
    {
        set<int, Compare> s( ( Compare() ) );
        
        vector<int> arr = {1,4,6,3,7,2,8,588,5};    
        for(int e : arr) s.insert(e);
        for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl;   
    
        return 0;
    }
    

    then the program compiles successfully and its output is

    588 8 7 6 5 4 3 2 1 
    

    That is as the second type template argument of the class template std::set you need to use the type specifier Compare. But if you want to specify the argument of the constructor you need to use an object of the type of the template argument.

    The same problem exists for the second code snippet. You need to write like it is shown in this demonstrative program.

    #include <iostream>
    #include <vector>
    #include <set>
    
    using namespace std;
    
    bool comp(const int &a, const int &b) { return a < b; }
    
    int main() 
    {
        set<int, bool ( * )( const int &, const int & )> s( comp );
        
        vector<int> arr = {1,4,6,3,7,2,8,588,5};    
        for(int e : arr) s.insert(e);
        for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl;   return 0;
    }
    

    The program output is the same as shown above.

    Pay attention to that the class template std::set is declared like

    template <class Key, class Compare = less<Key>, 
              class Allocator = allocator<Key> >
    class set;
    

    So if you specified explicitly the second template argument (instead of using by default the function object std::less<Key>) then you need to provide a corresponding object of this type in a constructor of the class template std::set.