12

We have a question, which discusses how to use a comparator in priority_queue in C++. He gives the overloaded operator class (or struct) as the third argument and it works fine. But bool function doesn't work. Why ? But it works fine in sort of the <algorithm>. When I looked into docs (priority_queue && algo/sort), both of them take the class Compare as their optional third argument.

#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <vector>

using namespace std;

bool cmp (const int &a,const int &b){ return a > b; }

struct cmp2
{
    bool operator() (const int &p1,const int &p2)
    {
        return p1 > p2;
    }
};

int main ()
{
//  freopen("test.txt","r",stdin);
    int a[10];
    vector<int> b(10);
    sort( a , a + 10, cmp );    // working cool
    sort( b.begin() , b.end() , cmp);   // working great
    priority_queue<int, vector<int> , cmp2 > x;    // as usual, working....
    priority_queue<int, vector<int> , cmp > y;   // not working why ?

    return 0;
}

Errors:

A:\pqvsarray.cpp    In function 'int main()':
27  40  A:\pqvsarray.cpp    [Error] type/value mismatch at argument 3 in template parameter list for 'template<class _Tp, class _Sequence, class _Compare> class std::priority_queue'
27  40  A:\pqvsarray.cpp    [Error] expected a type, got 'cmp'
27  43  A:\pqvsarray.cpp    [Error] invalid type in declaration before ';' token

So, why the difference ?

Community
  • 1
  • 1
azam
  • 269
  • 2
  • 6
  • 19
  • 1
    Because priority_queue template requires a type, but you are giving it a function pointer. – Selçuk Cihan Jan 18 '16 at 09:24
  • So, how / why does `sort` take a `function pointer` ? – azam Jan 18 '16 at 09:26
  • 1
    @SelçukCihan According to [the priority_queue documentation](http://www.cplusplus.com/reference/queue/priority_queue/), it can be a function pointer: *This can be a function pointer or a function object* – Aracthor Jan 18 '16 at 09:26
  • @azam because in sort, you are actually calling a method. However with the priority_queue, what you are doing is specifying what kind of a priority queue you want by giving the template parameters. That is the difference. – Selçuk Cihan Jan 18 '16 at 09:34

3 Answers3

8

You can use a function with std::priority_queue as well. The difference in what you're doing is that you pass the function to std::sort as a function parameter, but you try to define the function as a template parameter of the queue. This obviously does not work because the third argument is a type argument as the error explains. Besides, you cannot even have pointer or reference template arguments.

If you take a look at the reference, you'll find that the queue has a constructor for passing the comparison object. That's where you must pass the function.

There is a difference with std::sort. Sort is a function, and you can let the compiler deduce it's template arguments so you don't have to specify them explicitly. The queue is a class template, and template arguments of a class template can not be deduced (not in this context at least).

The template argument is defaulted to std::less<typename Container::value_type>, but you don't want to use that. Therefore, you must specify explicitly the type of the comparison object. You specify it where you're now trying to pass the object. How to get the type of the function pointer/reference, you may ask. You can do it like this: decltype(&cmp). If you have an outdated compiler that does not support decltype yet, then you'll need to specify the type without it: bool (&)(const int&, const int&).

Here is an example of how you would create a queue that uses your function.

std::priority_queue<int, std::vector<int>, decltype(&cmp)> x(cmp);
eerorika
  • 181,943
  • 10
  • 144
  • 256
  • 2
    Yes, `bool (&)(const int&, const int&)` as the third argument is working. `decltype` is in C++11 if I'm not wrong. And +1 for explaining well. – azam Jan 18 '16 at 10:29
  • Yup, `decltype` was added in C++11 indeed. – eerorika Jan 18 '16 at 11:06
2

As the error message implies, functions cannot be used as template parameters. The priority_queue will copy an object of the comparison type. For example, this might be std::less<int>, where an object of that type is std::less<int>(), and it being called is std::less<int>()(x, y). In C++11, you could use decltype, but in C++03 the "canonical" way is to create a "functor" (an entire type dedicated to being used as a function object.) This is one of the reasons why lambdas were created.

Community
  • 1
  • 1
  • 1
    Nice, for spotting the deficiency of C++ and explaining the reason for the advent of `lambda`s in C++11. – azam Jan 18 '16 at 10:35
1

The answer is in compiler errors. The third template parameter of priority queue is a comparator type (like structure or classes) and not a function.

Alexis Le Provost
  • 1,133
  • 2
  • 7
  • 9
  • 1
    According to [the priorty_queue documentation](http://www.cplusplus.com/reference/queue/priority_queue/), it should be possible to use a function. – Aracthor Jan 18 '16 at 09:25
  • 1
    @Aracthor: It is possible. You just have to pass the right argument to the right place. You pass the function pointer's type as a template argument for `priority_queue`, and you pass your actual function pointer as an argument to the constructor. This is the exact same situation as you have with `std::sort`, except there, the type is inferred, which you can do with a call to a function template (like `std::sort`), but not with an instantiation of a class template object (like `std::priority_queue`). – Benjamin Lindley Jan 18 '16 at 09:43
  • It would be better if you'd have explained a bit about the "*Why?*". But, thanks! – azam Jan 18 '16 at 10:37