简体   繁体   中英

Comparators in std::priority_queue

Is there a reason that the std::priority_queue 's constructor accepts a comparator by constant reference? What if the comparator goes out of scope?

I was thinking about this in the context of possibly moving the comparator as @LightnessRacesInOrbit pointed out!

I am sorry if there has already been a post about this. I have not been able to find it!

I have never actually thought about this before, and the const-ref indeed is a bit misleading. However, the function signature was thought up before move semantics came along and it became vogue to accept everything by value. Indeed, the comparator is copied!

[C++14: 23.6.4.1/4]: Effects: Initializes comp with x and c with y (copy constructing or move constructing as appropriate); calls c.insert(c.end(), first, last); and finally calls make_heap(c.begin(), c.end(), comp).

Lambdas are not copy-assignable, but they are copy-constructible, so there is no problem here.

[C++14: 5.1.2/20]: The closure type associated with a lambda-expression has a deleted (8.4.3) default constructor and a deleted copy assignment operator. It has an implicitly-declared copy constructor (12.8) and may have an implicitly-declared move constructor (12.8). [..]

Does this prevent move-construction of the comparator itself? Yes, it does. I'm going to assume that this convention, of taking the comparator by const-ref then copying it, stems from the STL days, way before move semantics. I imagine it wasn't seriously considered to add overloads to take the comparator by value because that adds complexity and you shouldn't have a complex, move-enhancible comparator in the first place (give them state, sure, but not too much ). Still, this may be worth raising with the committee if you can come up with a solid use case for moving a comparator.

It doesn't go out of scope -- it is copy constructed into the container. The description on cppreference.com states:

explicit priority_queue( const Compare& compare = Compare(),
                         const Container& cont = Container() );

Copy-constructs the underlying container c with the contents of cont. Copy-constructs the comparison functor comp with the contents of compare. Calls std::make_heap(c.begin(), c.end(), comp). This is also the default constructor.

There are various other forms of the constructor, but in all cases the internal comparator is either copy- or move-constructed from the one supplied.

std::priority_queue 's constructor makes a copy of the comparator provided, so it's not a problem if it goes out of scope.

You can use a lambda as comparator either by using std::function<bool(const T&, const T&)> as comparator type, or directly:

auto comp = [](int x, int y) { return x > y; };
std::priority_queue<int, std::vector<int>, decltype(comp)> q(comp);

You can facilitate this with a helper function:

template<typename T, typename Compare>
auto make_priority_queue(Compare&& comp) {
    return std::priority_queue<T, std::vector<T>, Compare>(std::forward<Compare>(comp));
}

int main() {
    auto q = make_priority_queue<int>([](int x, int y) { return x > y; });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM