The following code, which std::swap
s two std::priority_queue<int, std::vector<int>, decltype(some lambda)>
results in a compiler error in C++17, but not in C++20.
#include <queue>
int main()
{
auto cmp = [](int x, int y) {return x > y;};
std::priority_queue<int, std::vector<int>, decltype(cmp)> pq1(cmp), pq2(cmp);
std::swap(pq1, pq2); // adding this line results in an error
return 0;
}
The error under C++17 is
error: object of type 'value_compare' (aka '(the lambda cmp)') cannot be assigned because its copy assignment operator is implicitly deleted
{c = _VSTD::move(__q.c); comp = _VSTD::move(__q.comp); return *this;}
note: in instantiation of member function 'std::priority_queue<int, std::vector<int>, (lambda at main.cpp:13:16)>::operator=' requested here
__x = _VSTD::move(__y);
note: in instantiation of function template specialization 'std::swap<std::priority_queue<int, std::vector<int>, (lambda at main.cpp:13:16)>>' requested here
swap(pq1, pq2);
I feel the key here is that somehow, the lambda cmp’s copy assignment operator is implicitly deleted
. I’ve learned from the top answer at std::priority_queue syntax with lambda is confusing that C++20 made lambdas without default captures default-constructible (see cppreference source as well), but the issue here seems to be with copy-constructing, not default-constructing.
Why doesn’t the code compile under C++17, and what changes in C++20 let it compile?
>Solution :
"seems to be with copy-constructing": No, as the error message says it is with copy-assigning.
Swapping requires reassignment between the two objects, not only construction, because the two objects are not replaced by new ones. Only their values are exchanged.
Before C++20 lambdas were not assignable at all and so this can’t work. Since C++20 lambdas without capture are copy-assignable and so this works.
If you need a workaround for C++17, you can convert a non-generic lambda without captures to a function pointer which is copy-assignable (and -constructible). The function pointer conversion can be done explicitly or implicitly with the +
trick:
auto cmp = +[](int x, int y) {return x > y;};
std::priority_queue<int, std::vector<int>, decltype(cmp)> pq1(cmp), pq2(cmp);
std::swap(pq1, pq2); // works now
In C++20 you also don’t need to actually pass cmp
to the constructors, because lambdas without capture are now also default-constructible as you mentioned.