How to clear a priority_queue that uses user-defined compare?
From std::priority_queue documentation, I reduced the use of priority_queue to the case I need (= queue with user-defined compare)
>> cat test.cpp
#include <functional>
#include <queue>
#include <vector>
#include <iostream>
#include <utility>
auto queue_cmp = [](std::pair<int, double> const& lhs,
std::pair<int, double> const& rhs) {
return lhs.second > rhs.second; // Custom order.
};
typedef std::priority_queue<std::pair<int, double>,
std::vector<std::pair<int, double>>,
decltype(queue_cmp)> custom_queue;
template<typename T>
void print_queue(T q) { // NB: pass by value so the print uses a copy
int s = 0;
while(!q.empty()) {
std::pair<int, double> elem = q.top();
std::cout << s << ": " << elem.first << ", " << elem.second << std::endl;
q.pop();
s++;
}
std::cout << '\n';
}
int main() {
custom_queue q(queue_cmp);
for (int n = 10; n < 20; n++) {
double val = static_cast <double>(rand())/(static_cast<double>(RAND_MAX));
q.push(std::pair<int, double>(n, val));
}
print_queue(q);
//q = custom_queue(queue_cmp);
}
This runs OK
>> g++ -o test test.cpp
>> ./test
0: 15, 0.197551
1: 18, 0.277775
2: 16, 0.335223
3: 11, 0.394383
4: 19, 0.55397
5: 17, 0.76823
6: 12, 0.783099
7: 13, 0.79844
8: 10, 0.840188
9: 14, 0.911647
Now I need to reset q so, following priority queue clear method, I uncomment the last line in test.cpp... And get this compilation error as if I get this correctly, copy constructors are deleted on lamdba:
>> g++ -o test test.cpp
test.cpp: In function ‘int main()’:
test.cpp:34:31: error: use of deleted function ‘std::priority_queue<std::pair<int, double>, std::vector<std::pair<int, double> >, <lambda(const std::pair<int, double>&, const std::pair<int, double>&)> >& std::priority_queue<std::pair<int, double>, std::vector<std::pair<int, double> >, <lambda(const std::pair<int, double>&, const std::pair<int, double>&)> >::operator=(std::priority_queue<std::pair<int, double>, std::vector<std::pair<int, double> >, <lambda(const std::pair<int, double>&, const std::pair<int, double>&)> >&&)’
34 | q = custom_queue(queue_cmp);
| ^
In file included from /usr/include/c++/12/queue:64,
from test.cpp:2:
/usr/include/c++/12/bits/stl_queue.h:498:11: note: ‘std::priority_queue<std::pair<int, double>, std::vector<std::pair<int, double> >, <lambda(const std::pair<int, double>&, const std::pair<int, double>&)> >& std::priority_queue<std::pair<int, double>, std::vector<std::pair<int, double> >, <lambda(const std::pair<int, double>&, const std::pair<int, double>&)> >::operator=(std::priority_queue<std::pair<int, double>, std::vector<std::pair<int, double> >, <lambda(const std::pair<int, double>&, const std::pair<int, double>&)> >&&)’ is implicitly deleted because the default definition would be ill-formed:
498 | class priority_queue
| ^~~~~~~~~~~~~~
/usr/include/c++/12/bits/stl_queue.h:498:11: error: use of deleted function ‘<lambda(const std::pair<int, double>&, const std::pair<int, double>&)>&<lambda(const std::pair<int, double>&, const std::pair<int, double>&)>::operator=(const<lambda(const std::pair<int, double>&, const std::pair<int, double>&)>&)’
test.cpp:7:19: note: a lambda closure type has a deleted copy assignment operator
7 | auto queue_cmp = [](std::pair<int, double> const& lhs,
| ^
Is there a way to reset q in this case?
EDIT
bool queue_cmp(std::pair<int, double> const& lhs,
std::pair<int, double> const& rhs) {
return lhs.second > rhs.second; // Custom order.
};
Doesn't help
Not in C++17.
C++20 gives capture-less lambdas a default constructor. But in earlier language versions, the constructor is deleted.
Really, you should not be using a lambda. Just use a named struct.
As noted above in C++17 capture-less lambdas do not have default constructor.
Since a lambda is actually an anonymous class with a operator() method, you can use a named struct for the same purpose.
Your comparator will become:
struct queue_cmp
{
bool operator()(std::pair<int, double> const& lhs, std::pair<int, double> const& rhs) const
{
return lhs.second > rhs.second; // Custom order.
};
};
The rest of the code is almost the same.
You need to drop the decltype because queue_cmp is already a type, and there's no need to supply an instance of the comparator when constructing a queue:
typedef std::priority_queue<std::pair<int, double>,
std::vector<std::pair<int, double>>,
queue_cmp> custom_queue;
template<typename T>
void print_queue(T q) { // NB: pass by value so the print uses a copy
int s = 0;
while (!q.empty()) {
std::pair<int, double> elem = q.top();
std::cout << s << ": " << elem.first << ", " << elem.second << std::endl;
q.pop();
s++;
}
std::cout << '\n';
}
int main() {
custom_queue q;
for (int n = 10; n < 20; n++) {
double val = static_cast <double>(rand()) / (static_cast<double>(RAND_MAX));
q.push(std::pair<int, double>(n, val));
}
print_queue(q);
q = custom_queue{};
}
Demo: https://godbolt.org/z/7eq86r915.
Related
I found out by chance that I can have a const std::pair<const int, int>& reference to a std::pair<int,int>:
#include <utility>
int main()
{
std::pair<int, int> p{1,2};
const std::pair<const int, int>& ref_ok{p}; // why does this work?
std::pair<const int, int>& ref_invalid{p}; // then why does this not work?
}
Why is this possible, considering that const std::pair<const int, int> and std::pair<int, int> are different types that are not related by inheritance?
const std::pair<const int, int>& ref_ok{p}; is actually materializing a temporary std::pair<const int, int> initialized to the same values as p and the reference initialization is extending the lifetime of the temporary to that of the lifetime of the reference.
std::pair<const int, int>& ref_invalid{p}; is not allowed because non-const references can't bind to a temporary.
The following code example shows that ref_ok is not actually a reference to p. Changes to p have no effect on ref_ok.
#include <iostream>
#include <utility>
int main()
{
std::pair<int,int> p{1,2};
const std::pair<const int, int>& ref_ok{p}; // why does this work?
std::cout << p.first << ' ' << ref_ok.first << '\n';
p.first = 5;
std::cout << p.first << ' ' << ref_ok.first << '\n';
}
Output :
1 1
5 1
Live example : https://godbolt.org/z/8bfM7fYbx
I am getting below error:
:60:8: error: request for member 'push' in 'pq', which is of
non-class type 'std::priority_queue,
std::vector >, std::function, std::pair)> >(comparator)' 60 | pq.push(p1);
My code is as below:
Declared a comparator for priority_queue as below:
class comparator
{
bool operator ()(std::pair<int, int> &p, std::pair<int, int> &q)
{
return (p.second < q.second);
}
};
Declared priority queue as below:
std::priority_queue<std::pair<int, int>, vector<std::pair<int, int>>, std::function<bool(pair<int, int>, pair<int, int>)>> pq(comparator);
make pair as below:
auto p1 = make_pair(1, 3);
pushed it to priority_queue as:
pq.push(p1);
Can anyone please tell, what i am doing wrong here
The operator() must be public and should also be const qualified.
You don't need std::function here. Just supply the comparator as the third template parameter directly.
#include <queue>
#include <utility>
#include <vector>
struct comparator {
// must be public and should be const qualified:
bool operator()(std::pair<int, int> &p, std::pair<int, int> &q) const {
return p.second < q.second;
}
};
int main() {
std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>,
comparator> // <- the proper comparator
pq;
auto p1 = std::make_pair(1, 3);
pq.push(p1);
}
I have the following simplified files and classes:
Stat.h:
class Stat
{
auto getMinMaxValue(std::unordered_map< int, int >&);
};
Stat.cpp:
auto Stat::getMinMaxValue(std::unordered_map< int, int >&m)
{
return std::minmax_element(m.begin(), m.end(), [](const pair<int, int>& p1, const pair<int, int>& p2) { return p1.second < p2.second; });
}
StatCount.h:
class StatCount : public Stat
{
void setWeight(std::vector<D> const&, const std::string);
};
StatCount.cpp:
void StatCount::setWeight(vector<D> const& ref, const string type)
{
auto a = Stat::getMinMaxValue(m_value);
cout << "MIN: " << a.first->second << endl;
cout << "MAX: " << a.second->second << endl;
}
Since i declare the function "getMinMaxValue" into the base class Stat if i use the auto return type i got an error:
function 'getMinMaxValue' with deduced return type cannot be used before it is defined
but i failed to remove the auto return type and find the correct syntax to specify the return type of the method "getMinMaxValue"
if i read the documentation on cppreference i see it must be a pair of iterator but how ?
i respond to myself, it seems i have found the solution:
Stat.h:
std::pair<std::unordered_map< int, int >::iterator, std::unordered_map< int, int >::iterator> getMinMaxValue(std::unordered_map< int, int >&);
The below code snippet compiles with a very important warning.
#include <map>
#include <vector>
template <typename iterator>
const std::pair<int, float> &foo(iterator it) {
return *it;
}
int main() {
std::vector<std::pair<int, float>> vector;
std::map<int, float> map;
vector.push_back(std::make_pair(0, 0.0));
map.insert(std::make_pair(0, 0.0));
const std::pair<int, float> &r1 = foo(vector.begin());
const std::pair<int, float> &r2 = foo(map.begin());
if (r1 != r2) {
return 1;
}
return 0;
}
There is an implicit conversion from std::pair<const int, float> to std::pair<int, float> during foo(map.begin()) that creates a dangling reference.
ref2.cpp: In instantiation of ‘const std::pair<int, float>& foo(iterator) [with iterator = std::_Rb_tree_iterator<std::pair<const int, float> >]’:
ref2.cpp:16:52: required from here
ref2.cpp:7:11: warning: returning reference to temporary [-Wreturn-local-addr]
return *it;
^~
We could adjust the type of r2 to std::pair<const int, float> in this case. Nevertheless, it would be useful, in the general case, to assign the results of the two calls to foo() to type-compatible references. For example, the call to foo() might be wrapped in another function that always returns std::pair<int, float>&.
Can the reference assignment be made to operatate in a way that works around the misalignment of const modifiers?
Edit
The question is really about making std::pair<K,V> work with std::pair<const K,V>; vector<> and map<> are red-herrings. (In particular, see the discussion here about why the key in std::map<> is const.)
Better sample code might be:
#include <vector>
template <typename iterator>
const std::pair<const int, float>& bar(iterator it)
{
return *it;
}
int main()
{
const std::vector<std::pair<const int, float>> v1{ std::make_pair(0, 0.0f) };
bar(v1.begin());
const std::vector<std::pair<int, float>> v2{ std::make_pair(0, 0.0f) };
bar(v2.begin());
return 0;
}
According to your comments, what you're really trying to figure out is how to make the std::map<> iterator work like std::vector<>; the result should be a std::pair<> in both cases, not std::pair<const int, ...>.
With that, I've written this hack; I'm sure it's got problems and/or could be improved:
const auto& remove_const(const std::pair<const int, float>& p) {
return reinterpret_cast<const std::pair<int, float>&>(p); // :-(
}
template <typename iterator>
const std::pair<int, float> &foo(iterator it) {
return remove_const(*it);
}
You might change:
template <typename iterator>
const std::pair<int, float> &foo(iterator it) {
return *it;
}
to:
template <typename iterator>
decltype(auto) foo(iterator it) {
return *it;
}
this requires c++14, to stay with c++11 use:
auto foo(iterator it) -> decltype(*it) {
While practicing the use of lambdas, I wrote this program which is supposed to sort a list of pairs by their second element (an int).
#include <iostream>
#include <algorithm>
#include <list>
using namespace std;
int main()
{
list<pair <string, int>> s = {{"two", 2}, {"one", 1}, {"three", 3}};
sort(s.begin(), s.end(), [](pair<string,int> a, pair<string, int> b) -> bool {
return (a.second) > (b.second);
});
for_each(s.begin(), s.end(), [](pair<string, int> a) {
cout << a.first << " " << a.second << endl;
});
}
I get those errors, though:
c:\qt\qt5.2.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\stl_algo.h:5513: error: no match for 'operator-' (operand types are 'std::_List_iterator<std::pair<std::basic_string<char>, int> >' and 'std::_List_iterator<std::pair<std::basic_string<char>, int> >')
std::__lg(__last - __first) * 2, __comp);
^
c:\qt\qt5.2.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\stl_algo.h:2245: ошибка: 'void std::__final_insertion_sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = std::_List_iterator<std::pair<std::basic_string<char>, int> >; _Compare = main()::__lambda0]', declared using local type 'main()::__lambda0', is used but never defined [-fpermissive]
__final_insertion_sort(_RandomAccessIterator __first,
^
What is wrong with my code?
You may not use std::sort with sequential containers such as std::list or std::forward_list because they have no random access iterator that is required by the standard algorithm std::sort. By this reason the both containers have their own member functions sort.
In you case the code will look the following way:
#include <iostream>
#include <list>
#include <string>
using namespace std;
int main()
{
list<pair <string, int>> s = {{"two", 2}, {"one", 1}, {"three", 3}};
s.sort( []( const pair<string,int> &a, const pair<string,int> &b ) { return a.second > b.second; } );
for ( const auto &p : s )
{
cout << p.first << " " << p.second << endl;
}
}
Take into account that you need to include header <string> otherwise your program will not be compiled with other compilers.
std::sort requires random access iterators, which std::list does not have. But you can use std::list::sort instead.
s.sort([](const pair<string,int>& a, const pair<string,int>& b)
{
return (a.second) > (b.second);
});
where I have made the parameters of the predicate const references, since there is no need to copy them, and doing so might incur some unnecessary overhead.