Custom ordering for std::priority_queue [duplicate] - c++

This question already has answers here:
declaring a priority_queue in c++ with a custom comparator
(11 answers)
Closed 11 days ago.
I'm trying to make a program that has a std::priority_queue of objects. I want the queue to order the objects based on A::num.
This is the code:
#include <iostream>
#include <queue>
#include <vector>
class A {
public:
int num;
A (int n) {
this->num = n;
}
~A() {
std::cout << "Deleting an A\n";
}
};
struct Compare {
bool operator()(const A* first, const A* second) {
return first->num < second->num;
}
};
int main() {
std::priority_queue AContainer(A*, std::vector<A*>, Compare);
AContainer.push(new A(4));
AContainer.push(new A(8));
AContainer.push(new A(6));
while (AContainer.size() < 0) {
A* del = AContainer.top();
delete del;
del = nullptr;
AContainer.pop();
}
return 0;
}
The compiler returns an error, however I'm not sure why or where it is referring to, or how to fix it:
error: deduced class type 'priority_queue' in function return type
24 | std::priority_queue AContainer(A*, std::vector<A*>, Compare);
| ^~~~~~~~~~
In file included from /usr/include/c++/11/queue:64,
from /tmp/HvPOYonaOt.cpp:3:
/usr/include/c++/11/bits/stl_queue.h:456:11: note: 'template<class _Tp, class _Sequence, class _Compare> class std::priority_queue' declared here
456 | class priority_queue
| ^~~~~~~~~~~~~~
If you could help me out with this that would be great.

A*, std::vector<A*>, Compare are types that you need to supply to the template parameters of std::priority_queue, not values you supply to a constructor.
std::priority_queue<A*, std::vector<A*>, Compare> AContainer;
See it on coliru

Alternative fix using C++17 Class template argument deduction:
std::priority_queue AContainer{Compare{}, std::vector<A*>{}};
Which is closer to your example.
https://godbolt.org/z/547Tq1EnY

In this line you use invalid syntax for instantiating a templated class:
std::priority_queue AContainer(A*, std::vector<A*>, Compare);
The proper syntax is:
std::priority_queue<A*, std::vector<A*>, Compare> AContainer;
Having written this, in modern c++ it is recommended to avoid using raw pointers with manual new/delete whenever possible.
If you can simply store A instances this would be the simplest solution.
If you need pointers (e.g. for using polymorphism, which is not shown in your question), you can use smart pointers, e.g. std::unique_ptr:
#include <iostream>
#include <queue>
#include <vector>
class A {
public:
int num;
A(int n) {
this->num = n;
}
~A() {
std::cout << "Deleting an A\n";
}
};
struct Compare {
bool operator()(std::unique_ptr<A> const & first, std::unique_ptr<A> const & second) {
return first->num < second->num;
}
};
int main() {
std::priority_queue<std::unique_ptr<A>, std::vector<std::unique_ptr<A>>, Compare> AContainer;
AContainer.push(std::make_unique<A>(4));
AContainer.push(std::make_unique<A>(8));
AContainer.push(std::make_unique<A>(6));
while (AContainer.size() > 0) {
AContainer.pop();
}
return 0;
}
Note that you don't have to new or delete any object manually.
A side note: I think you have a typo in your while condition - while (AContainer.size() < 0) should be while (AContainer.size() > 0).

Related

find_if() with template?

I'm new at C++ and I'm trying to use find_if with templates but it doesn't seem to work the way I want it to. Why is that? I tried to find the answer in previous asked questions about templates with iterators, but I guess I missed the right one or maybe just didn't understand the answers correctly. I tried to use typename before iterator, but that didn't change the error-message.
Is there a better way to do this and if so, can someone help me to learn how to do this?
(error message: error C3867: 'UserInterface::Number': function call missing argument list, use '&Userinterface::Number' to create a pointer to member) =
When that happens, I know that I have missed () after the function call, but thats not the case this time?!
#include <iostream> // std::cout
#include <algorithm> // std::find_if
#include <vector> // std::vector
template<typename T>
class UserInterface
{
public:
bool Number(int i);
void function();
};
template<typename T>
bool UserInterface<T>::Number(int i) {
return (i >= 40);
}
template<typename T>
void UserInterface<T>::function()
{
std::vector<T> myvector;
myvector.push_back(10);
myvector.push_back(25);
myvector.push_back(15);
myvector.push_back(55);
myvector.push_back(1);
myvector.push_back(65);
myvector.push_back(40);
myvector.push_back(5);
std::vector<T>::iterator it = std::find_if(myvector.begin(), myvector.end(), Number);
std::cout << "The first value over 40 is " << *it << '\n';
std::cin.get();
}
int main() {
UserInterface<int> fu;
fu.function();
return 0;
}
There are a few problems with your example. The first is that std::find_if is incompatible with non-static member method pointers. Those pointers would require a this to work. Since UserInterface::Number doesn't access any non-static members and doesn't call any non-static methods, you can just make it static.
The second issue is that you must use & to obtain a pointer to your function.
Finally, don't forget typename before std::vector<T>::iterator.
#include <iostream> // std::cout
#include <algorithm> // std::find_if
#include <vector> // std::vector
template<typename T>
class UserInterface
{
public:
static bool Number(int i);
// ^^^^^^ Add static here
void function();
};
template<typename T>
bool UserInterface<T>::Number(int i) {
return (i >= 40);
}
template<typename T>
void UserInterface<T>::function()
{
std::vector<T> myvector;
myvector.push_back(10);
myvector.push_back(25);
myvector.push_back(15);
myvector.push_back(55);
myvector.push_back(1);
myvector.push_back(65);
myvector.push_back(40);
myvector.push_back(5);
typename std::vector<T>::iterator it =
// ^^^^^^^^ typename here
std::find_if(myvector.begin(), myvector.end(), &Number);
// ^
std::cout << "The first value over 40 is " << *it << '\n';
std::cin.get();
}
int main() {
UserInterface<int> fu;
fu.function();
return 0;
}

how to trivially adapt set or map ordering predicate for pointers

There must be a trivial answer to this...
I have a std::set or a std::map or some object type which has a natural ordering - say std::less.
I need to change my set or map to contain shared_ptr instead of copies of T.
So I want something like:
using my_set std::set<std::shared_ptr<T>, std::less<*T>>;
But I'm drawing a blank as to how to specify "use the less adaptor on ____ adaptor of T so that it's on dereferenced members, not on shared_ptrs!"
Is there a std::less<std::dereference<std::shared_ptr<T>>> equivalent?
There is currently no functor in the C++ standard library to achieve what you want. You can either write a custom comparator, or if you need this functionality often, come up with an indirect/dereference function object.
Related and potentially helpful threads; the first one offers a generic solution for many operators (even if it requires a bit of code):
Why do several of the standard operators not have standard functors?
Functor that calls a function after dereferencing?
Less-than function dereferencing pointers
While the standard library may not already provide what you need, I think it's pretty trivial to write your own std::dereference_less:
#include <memory>
#include <set>
namespace std
{
template<typename T>
struct dereference_less
{
constexpr bool operator ()(const T& _lhs, const T& _rhs) const
{
return *_lhs < *_rhs;
}
};
}
int main()
{
using key_type = std::shared_ptr<int>;
std::set<key_type, std::dereference_less<key_type>> mySet;
}
Demo (refactored a bit to have a template type alias like in your question)
Since you are already changing your internal interface to something that requires dereferencing you could also just write a wrapper class and provide a bool operator< () as follows:
#include <memory> // shared_ptr
#include <set> // set
#include <iostream> // cout
using namespace std;
template<typename T>
class wrapper
{
public:
shared_ptr<T> sp;
bool operator< (const wrapper<T>& rhs) const
{
return *( sp.get() ) < *( rhs.sp.get() ) ;
}
wrapper(){}
wrapper(shared_ptr<T> sp):sp(sp){}
};
int main()
{
shared_ptr<int> sp1 (new int);
*sp1 = 1;
shared_ptr<int> sp2 (new int);
*sp2 = 2;
set<wrapper<int>> S;
S.insert(wrapper<int>(sp2));
S.insert(wrapper<int>(sp1));
for (auto& j : S)
cout << *(j.sp) << endl;
return 0;
}

Sorting a std::vector of std::pair's using user defined compare class

I have two class templates MyClassA<T> and MyClassB<T>.
From these, I have constructed two std::vector's as std::vector<MyClassA<double>> A and std::vector<MyClassB<double>> B.
My goal is to first sort A in ascending order (in actual I will do a range/partial sort).
Then using that order to sort B.
What I am doing so far is the following:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <random>
// my class definitions
template<typename T>
class MyClassA
{
public:
T valA;
};
template<typename T>
class MyClassB
{
public:
T valB;
};
// my compare class
template<typename T>
using TIter = typename std::vector<T>::const_iterator;
template <typename T>
class MyCompare
{
public:
bool operator()(std::pair<std::size_t, TIter<MyClassA<T>>>
const& a, std::pair<std::size_t, TIter<MyClassA<T>>> const& b)
{
return *(a.second).valA < *(b.second).valA;
}
};
// sort from given order
//... not yet implemented
int main()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(0, 1);
// first ClassA Object vector A
std::vector<MyClassA<double>> A(5);
for(auto& i:A) i.valA = dis(gen);
// second ClassB Object vector B
std::vector<MyClassB<double>> B(5);
for(auto& i:B) i.valB = dis(gen);
// sort vector A elements' references in ascending order
std::size_t i = 0;
std::vector<std::pair<std::size_t, TIter<MyClassA<double>>>> torder(A.size());
for(auto it = A.begin(); it != A.end(); ++it, ++i) torder[i] = std::make_pair(i, it);
std::sort(torder.begin(), torder.end(), MyCompare<double>()); // getting error here
// sort vectors A and B elements using the above sorted order
// ...
return 0;
}
However, I am getting the following error:
error: 'const class __gnu_cxx::__normal_iterator<const MyClassA<double>*, std::vector<MyClassA<double> > >' has no member named 'valA'
It's a simple case of problem with the operator precedence. The member selection dot . has higher precedence than the dereference operator, so e.g. *(a.second).valA is parsed as *((a.second).valA).
Simply change to e.g. a.second->valA (or (*a.second).valA).
return *(a.second).valA < *(b.second).valA;
a.second and b.second appears to be iterators, so this should simply be:
return (a.second)->valA < (b.second)->valA;

Priority queue of struct's pointers

I know that there are similar threads but after spending an hour trying to force my program to work, I decided to ask for a help.
First of all. I've thought that I know c++ pretty well since I tried something which is very simple in PHP(programming language which I know best) but very complexed in c++ (at least very complexed for me). So I want to create priority_queue of struct's pointers. It's obvious that I need to create my own compare function. So I tried this code:
#include <iostream>
#include <list>
#include <queue>
using namespace std;
typedef struct MI
{
int nr;
int koszt;
bool operator<(const MI& a, const MI& b) {
return a.koszt > b.koszt;
}
} miasto, *miasto_wsk;
int main()
{
priority_queue<miasto_wsk> q;
miasto_wsk mi;
mi = new miasto;
mi->nr = 1;
mi->koszt = 2;
q.push(mi);
}
And when I tried to compile my program I ended up with compilation error:
test.cpp:11:44: error: ‘bool MI::operator<(const MI&, const MI&)’ must take exactly one argument
Can you explain me what I'm doing wrong and explain me how all this stuff with structs compare works(or give me a good tutorial/article which explains that from the beginning)
EDIT:
I changed my code to this:
#include <iostream>
#include <list>
#include <queue>
using namespace std;
typedef struct miasto
{
int nr;
int koszt;
} *miasto_wsk;
bool myComparator(miasto_wsk arg1, miasto_wsk arg2) {
return arg1->koszt < arg2->koszt; //calls your operator
}
int main()
{
priority_queue<miasto_wsk, vector<miasto_wsk>, myComparator> q;
miasto_wsk mi;
mi = new miasto;
mi->nr = 1;
mi->koszt = 2;
q.push(mi);
}
And now I getting this error msg:
test.cpp: In function ‘int main()’:
test.cpp:19:64: error: type/value mismatch at argument 3 in template parameter list for ‘template<class _Tp, class _Sequence, class _Compare> class std::priority_queue’
test.cpp:19:64: error: expected a type, got ‘myComparator’
test.cpp:19:67: error: invalid type in declaration before ‘;’ token
test.cpp:24:7: error: request for member ‘push’ in ‘q’, which is of non-class type ‘int’
What is the problem? Maybe I should use copies of structs instead pointers to structs?
EDIT2
This code doesn't produce any compilation errors:
#include <iostream>
#include <list>
#include <queue>
using namespace std;
typedef struct miasto
{
int nr;
int koszt;
bool operator< (const miasto& rhs)
{
koszt > rhs.koszt;
}
} *miasto_wsk;
int main()
{
priority_queue<miasto_wsk> q;
miasto_wsk mi;
mi = new miasto;
mi->nr = 1;
mi->koszt = 22;
q.push(mi);
}
So #Angew idea seems to be wrong.
EDIT3:
This is my final code. It not only compile without errors but also doing exactly what I want. Thank you so much #Angew
#include <iostream>
#include <list>
#include <queue>
using namespace std;
typedef struct miasto
{
int nr;
int koszt;
} *miasto_wsk;
struct MyComparator {
bool operator() (miasto_wsk arg1, miasto_wsk arg2) {
return arg1->koszt > arg2->koszt; //calls your operator
}
};
int main()
{
//priority_queue<miasto_wsk, vector<miasto_wsk>, myComparator> q;
priority_queue<miasto_wsk, vector<miasto_wsk>, MyComparator> q;
miasto_wsk mi;
mi = new miasto;
mi->nr = 1;
mi->koszt = 22;
q.push(mi);
miasto_wsk mi1;
mi1 = new miasto;
mi1->nr = 2;
mi1->koszt = 50;
q.push(mi1);
miasto_wsk mi2;
mi2 = new miasto;
mi2->nr = 3;
mi2->koszt = 1;
q.push(mi2);
cout << q.top()->koszt << endl;
q.pop();
cout << q.top()->koszt << endl;
q.pop();
cout << q.top()->koszt << endl;
q.pop();
}
There are multiple issues here.
When you define an operator inside a class, it automatically takes a parameter of the class type as its first argument, and you must not create a parameter for it. So you either keep the operator in the class, like so:
struct MI {
bool operator< (const MI&);
};
or declare the operator as free-standing:
struct MI {
//...
};
bool operator< (const MI&, const MI&);
Second, your priority_queue stores pointers to MI, not instances of MI, so the operator will not be called anyway. You must provide a comparator when defining the priority queue, like this (EDITED):
struct MyComparator {
bool operator() (miasto_wsk arg1, miasto_wsk arg2) {
return *arg1 < *arg2; //calls your operator
}
};
int main() {
priority_queue<miasto_wsk, vector<miasto_wsk>, MyComparator> q;
//...
}
Third is just a style thing: I'd suggest you name the class directly miasto rather than making it just a typedef. It's more natural in C++.
The error, if you read it again, tells you exactly what's wrong: That the MI::operator< function should take only one argument instead of two.
If you have operator< in the class (like you do) then the function takes only one argument and that is the other object to compare this with. If you create operator< as a free standing function (i.e. not part of the class) then it has to take two arguments.
Your comparison operator is a member function, so it should only take one parameter, for theRHS:
bool operator<(const MI& rhs) {
koszt > rhs.koszt;
}
Another option is to declare it as a non-member function:
struct MI {};
bool operator<(const MI& a, const MI& b) {
return a.koszt > b.koszt;
}
Use friend keyword to put the operator < in the global scope
typedef struct MI
{
int nr;
int koszt;
friend bool operator<(const MI& a, const MI& b)
{
return a.koszt > b.koszt;
}
} miasto, *miasto_wsk;

C++ Generic code for deleting pointer value in Map and vector of pointers

I have some generic code for deleting pointers within a vector or a value of a Map.
Is there a better way of doing this (without using shared_ptrs or any o fthe tr1 extensions )?
Also is the code correct?
Here is my code:
I have a namespace
#ifndef CONTAINERDELETE_H
#define CONTAINERDELETE_H
#include <functional>
#include <map>
#include <vector>
#include <algorithm>
namspace ContainerDelete{
template<class A, class B>
struct DeleteMap
{
bool operator()( pair<A,B> &x) const
{
delete x.second;
return true;
}
};
template<class T>
struct DeleteVector
{
bool operator()(T &x) const
{
delete x;
return true;
}
};
}
#endif
I would then use this namespace in some bit of code to delete a map or vector.
Test Map deletion.
#include "ContainerDelete.h"
using namespace std;
// Test function.
void TestMapDeletion()
{
// Add 10 string to map.
map<int,B*> testMap;
for( int Idx = 0; Idx < 10; ++Idx )
{
testMap[Idx] = new B();
}
// Now delete the map in a single line.
for_each( testMap.begin(),
testMap.end(),
ContainerDelete::DeleteMap<int,B*>());
}
Test Vector Deletion
// Test Function.
void TestVectorDeletion()
{
// Add 10 string to vector.
vector<B*> testVector;
for( int Index = 0; Index < 10; ++Index )
{
testVector.push_back( new B());
}
// Now delete the vector in a single line.
for_each( testVector.begin(),
testVector.end(),
ContainerDelete::DeleteVector<B*>());
}
Thanks,
Mike
Better would be if reduce the genericity as:
struct DeleteVector
{
template<class T> //use the template here!
void operator()(T &x) const
{
delete x;
}
};
if you do so, then you could simply write this:
for_each(testVector.begin(),
testVector.end(),
ContainerDelete::DeleteVector());
No need to pass type argument when you use DeleteVector, for it is not a class template anymore!
Similarly, you can implement DeleteMap functor.
You should also rename DeleteVector to DeleteT, and DeleteMap to DeletePairSecond, as both of these can be used more generically. For example, DeleteT can be used even with std::list, or even with arrays.
The code is ok. I can't imagine any other ways to delete the pointers. All you can do is to reduce explicit type specification like in upper question. I know one more uglier way to do it: functions deduce types of their template parameters. So you can write template function with the first argument - vector, second - ptr and then use std::bind of vector parameter to make this function accepting one parameter - ptr.
But functor is better and more flexible.