1

I'm trying to declare a priority queue as a private member so all other methods in the class can access it.

However, I am not able to get this to work with a custom lambda compare function.

Moreover, what is the recommended way of handling such situations?

This works:

private:
    priority_queue<int, vector<int>, greater<int>> pq;

This does not work.

 private:
        static auto comp = [](int n1, int n2) {return (n1 > n2);};
        priority_queue<int, vector<int>, decltype(comp)> pq;

If I want to use an STL object with a custom compare function that is accessible by all class methods, how would I do so?

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • 2
    I strongly recommend creating a [mre] and reproducing the error messages you receive exactly. I suspect this has nothing to do with the lambda in the `priority_queue` and is actually a problem with how you initialize `comp`. – user4581301 Mar 15 '22 at 20:00
  • Welcome to SO. Please take the [tour], read [ask] and most importantly, how to make a [mre]. – super Mar 15 '22 at 20:01
  • Side note: I don't think this is worth the trouble. Since there's no state in the lambda and the scaffolding needed to get the lamda working is at least as annoying to write as a function object this is a case where I'd define `struct comp { bool operator()(int n1, int n2) {return (n1 > n2);} };` and just walk away with `priority_queue, comp> pq;` – user4581301 Mar 15 '22 at 20:13

1 Answers1

3

The problem here is a fairly subtle one. Before C++ 20, lambdas are not default constructible, but from C++ 20 on, they are. So looking at this:

priority_queue<int, vector<int>, decltype(comp)> pq;

we can see that pq only knows the type of your comparator and will need to instantiate that type itself. But, before C++20, it cannot because the language doesn't support it.

So, there are two solutions to this, both equally easy:

  1. Add -std=c++20 to your compiler switches.

  2. Change that line of code to:

priority_queue<int, vector<int>, decltype(comp)> pq { comp };

And in fact for option 2, since you are (also) initialising comp inline, you also need:

static constexpr auto comp = [] (int n1, int n2) { return n1 > n2; };

or:

static inline auto comp = [] (int n1, int n2) { return n1 > n2; };

Both of which need C++17 or later.

Now you have passed as instance of comp to priority_queue's (alternative) constructor and so it has what it needs to get the job done.

The reason that std::greater works is that it's a just a normal functor that std::priority_queue can construct an instance of when it needs to.

I would also say that it's worth reading this.

And just to round off, the nice thing about using C++ 20 is that, for one-off cases, you can do things like this:

std::priority_queue<int, std::vector<int>, decltype ([] (int n1, int n2) { return n1 > n2; })> pq;

Which keeps it all neat and tidy.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • Never heard the term "proper" used to distinguish function objects fully described by their type and not by an instance (which has implications not only for default construction but also copy... for a function object with state it generally is only moveable). I would go with "stateless". – Ben Voigt Mar 15 '22 at 20:41
  • 1
    @BenVoigt but a captureless lambda *is* stateless as well. – Quentin Mar 15 '22 at 20:42
  • @Quentin: And that's why they can have a default constructor (and in C++20 they do). A stateful function object generally has to use the particular instance passed in (potentially moved) and not just make copies or new default-constructed instances at need. – Ben Voigt Mar 15 '22 at 20:44
  • Thank you! cppreference was above my head, your explanation was exactly the direction I needed. – throwaway15032022 Mar 15 '22 at 22:34
  • Don't worry, the time for using cppreference will come. In the meantime, you might take a look at [this list](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) – Paul Sanders Mar 15 '22 at 22:56