Priority queue of struct's pointers - c++

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;

Related

Class with an unordered set of itself

I simplified my question to this. Can I create a class that has an unordered set with template type itself? To be specific, for example a Square that has a pointer to an unordered set of neighbors. I got stuck trying to integrate a hash function with the class itself.
Here is my code:
#include <iostream>
#include <unordered_set>
#define SIZE 200
#define MASTER 0
class Square;
namespace std{
template<>
struct hash<Square> {
std::size_t operator () (Square const &v) const
{
return v.r;
}
};
}
class Square{
public:
int c1, c2;
int r;
std::unordered_set<Square> *neigh;
Square() {
neigh = new std::unordered_set<Square>();
}
~Square(){
delete neigh;
}
bool operator==(const Square& second) {
return this->r == second.r
&& this->c1 ==second.c1
&& this->c2 == second.c2;
}
};
int main(int argc, char *argv[]) {
Square sq;
Square tt;
sq.neigh->insert(tt);
}
I tried to compile using g++ and FLAGS = --std=c++17 -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -ggdb. The error received was gigantic, starting with:
test.cpp: In member function ‘std::size_t std::hash<Square>::operator()(const Square&) const’:
test.cpp:15:20: error: invalid use of incomplete type ‘const class Square’
15 | return v.x;
I don't know what is the correct approach to this situation. Please take into consideration this is my simplified code version of what I need, so I really need a neighbors field.
To solve the problem you're asking about, just declare std::hash<Square>::operator() before the Square definition, but don't implement it:
namespace std{
template<>
struct hash<Square> {
std::size_t operator() (Square const &) const;
};
}
Then after the Square definition, define the std::hash<Square>::operator():
namespace std {
std::size_t hash<Square>::operator() (Square const& v) const
{
// return calculation
}
}
You have a problem with the insert too. You copy an object with a pointer and then destroy the same pointer twice. To remedy that, use a std::unique_ptr<std::unordered_set<Square>> which helps since you'll get a compilation error if you try copying it.
class Square{
public:
std::unique_ptr<std::unordered_set<Square>> neigh;
Square() : neigh{std::make_unique<std::unordered_set<Square>>()} {}
// no destructor needed
bool operator==(const Square& second) const { // should be const
// ...
}
};
You then have to move objects into place:
sq.neigh->insert(std::move(tt));
or emplace them:
sq.neigh->emplace(...constructor arguments...);
Demo

vector of pair of set with lambda comparator

I'm trying to make a vector of pairs of set: vector<pair<set<int>, set<int>>> but I want to use different lambda comparators for the two sets.
I tried doing:
#include <bits/stdc++.h>
using namespace std;
int main() {
auto cmp = [&] (int a, int b) -> bool {
return a > b;
};
auto hi = [&] (int a, int b) -> bool {
return a < b;
};
vector<pair<set<int, decltype(cmp)>, set<int, decltype(hi)>>> lol(cmp, hi);
return 0;
}
but it gave me this error:
test.cpp:11:75: error: no matching function for call to ‘std::vector<std::pair<std::set<int, main()::<lambda(int, int)> >, std::set<int, main()::<lambda(int, int)> > > >::vector(main()::<lambda(int, int)>&, main()::<lambda(int, int)>&)’
ype(cmp)>, set<int, decltype(hi)>>> lol(cmp, hi);
^
compilation terminated due to -Wfatal-errors.
Also, is there any way to initialize the size of the vector as well?
Please help.
You are trying to pass your lambdas to the constructor of the outer vector, which has no constructors that take lambdas as input.
You need to pass the lambdas to the std::set constructor instead, which means you need to construct the individual std::set instances (see C++11 std::set lambda comparison function) and then push them into the vector, eg:
#include <vector>
#include <set>
#include <utility>
using namespace std;
auto cmp = [] (int a, int b) -> bool {
return a > b;
};
auto hi = [] (int a, int b) -> bool {
return a < b;
};
using set_cmp = set<int, decltype(cmp)>;
using set_hi = set<int, decltype(hi)>;
using set_pair = pair<set_cmp, set_hi>;
int main()
{
vector<set_pair> lol;
...
lol.push_back(make_pair(set_cmp(cmp), set_hi(hi)));
...
return 0;
}
This means you would not be able to pre-size the vector, since it would need to be able to default-construct the set objects, thus you couldn't pass your lambdas to them. If you want that, use stateless functors instead:
#include <vector>
#include <set>
#include <utility>
using namespace std;
struct cmp {
bool operator()(int a, int b) const {
return a > b;
}
};
struct hi {
bool operator()(int a, int b) const {
return a < b;
}
};
using set_cmp = set<int, cmp>;
using set_hi = set<int, hi>;
using set_pair = pair<set_cmp, set_hi>;
int main()
{
vector<set_pair> lol(...desired size...);
...
return 0;
}

How to sort an array of struct/class based on its member data?

How to sort an array of struct/class based on its member data, as this fails ?
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct O{
const string n;
int a=1;
};
bool bfunction (O a, O b) {
return a.n < b.n; }
int main () {
O m[]={ {"unta"}, {"jalan"}, {"sama"}, {"aki"} };
// using function in sort control
sort (m.begin(), m.end(), &bfunction);
}
gcc gives:
error: request for member ‘begin’ in ‘m’, which is of non-class type ‘O [4]’
sort (m.begin(), m.end(), &bfunction);
^~~~~
error: request for member ‘end’ in ‘m’, which is of non-class type ‘O [4]’
sort (m.begin(), m.end(), &bfunction);
^~~~~
sincere useful help is appreciated
Use std::array
#include <iostream>
#include <algorithm>
#include <vector>
#include <array>
struct O {
std::string n;
int a;
O(const char* c) : a(1) { n = c; }
};
bool bfunction(O a, O b) {
return a.n < b.n;
}
int main() {
std::array<O, 4> m = { "unta", "jalan", "sama", "aki" };
// using function in sort control
std::sort(m.begin(), m.end(), &bfunction);
}
Some mistakes are made here:
sort (m.begin(), m.end(), &bfunction); calls begin() and end() on an O[]. But an array has no member functions whatsoever.
You got some choice here: Either make m an std::array<O, 4> or a std::vector<O> or use std::begin(m) and std::end(m) which work for static arrays.
The sorting function should take it's parameters via const reference:
bool bfunction (const O &a, const O &b)
In the sorting function a.n < b.n compares two arrays of strings, but such a comparison is not defined anywhere. Thats a logic error you need to solve. Think about what you actually want to compare here. Comparison is defined for std::string, for example return a.n[0] < b.n[0]; would work.
When sorting anything elements need to be moved around. But your struct O has no move constructor, because you don't provide one and the automatically generated one would be ill formed because O has const members.
I think the best way to deal with this is to make all member variables private and control access to them via getters and setters. For now, the easiest way is to just remove the const.
Here is a copy-paste-ready example of how I'd do it (assuming your n should simply be a string, rather than an array of seven strings). If you really want to have an array of strings as n, then you have to define a proper ordering for them.
#include <iostream>
#include <algorithm>
#include <vector>
#include <array>
class O {
std::string n;
int a;
public:
O(const char* c, int a = 1) : n(c), a(a) {}
const std::string& get_n() const { return n; }
};
bool bfunction(const O& a, const O& b) {
return a.get_n() < b.get_n();
}
int main() {
std::array<O, 4> m = { "unta", "jalan", "sama", "aki" };
std::sort(m.begin(), m.end(), &bfunction);
for(auto& x : m) {
std::cout << x.get_n() << ',';
}
}
Live code here.

Initializing a 2D array member of a class in a constructor

I am getting a compile error setting a 2D array class member in the constuctor:
#include <queue>
using namespace std;
#define N 11
struct Elem {
Elem(uint32_t row, uint32_t col)
: row_(row), col_(col)
{ }
uint32_t row_, col_;
};
class Mycomp {
public:
Mycomp(int arr[][N])
{
arr_ = arr;
}
bool operator() (const Elem &lhs, const Elem &rhs)
{
return arr_[lhs.row_][lhs.col_] > arr_[rhs.row_][rhs.col_];
}
int arr_[][N];
};
int *mergeKArrays(int arr[][N], int k)
{
Mycomp mycomp(arr);
priority_queue<Elem, vector<Elem>, Mycomp> pq(mycomp);
for (uint32_t i = 0; i < k; ++i) {
pq.push(Elem(i, 0));
}
return (int *) arr;
}
int main() { }
I am getting the following error:
./mergek.cc: In constructor ‘Mycomp::Mycomp(int (*)[11])’:
./mergek.cc:23:22: error: incompatible types in assignment of ‘int (*)[11]’ to ‘int [0][11]’
arr_ = arr;
^
I have tried different variations, e.g. "&arr_[0] = arr;" did not work.
Thank you,
Ahmed.
Try to avoid using C style arrays and start using C++ containers like std::vectors, std::array, std::maps, etc.
In your code you tried to directly assign a array, which is not according to the rules and hence error would be lvalue must be modifiable value.
This problem can be rectified by visiting
error : expression must be a modifiable lvalue

Passing compare function for a generic class

I want to create a priority queue for which I am using a heap(using array).The priority queue will be generic thus accept all data types as long as the client pass a compare function through constructor to compare the two types.
How can I create a constructor that will accept the compare function as a parameter? Moreover how can I make the compare function to be called when I check
return (Type a==Type b)
Eg.
struct node{
string val1;
string val2;
vector<node *> connectedNodes;
};
int compareNode(node a,node b){
//describe the compare
}
int main(){
PQueue<node> q(compareNode);
}
The PQueue class is implemented as an array. As the adding,bubbling-up, heapifying needs to compare two ValType I want them to compare using compareNode.
You don't have to do this: don't use an array, use the built-in priority-queue of the STL library in c++. It has its own compare function which you can alter.
Reference: http://www.cplusplus.com/reference/queue/priority_queue/
You can also see topcoder tutorials (for algorithmic usage).
Let me first give you a simple answer and then a more versatile one.
You can simply pass a function as parameter by declaring the type of that parameter to be the type of pointer function. You can also have variables of type pointer to function. For instance, if the declaration of your function is
int compareNode(node a, node b)
then you could do something like this:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
struct node{
string val1;
string val2;
vector<node *> connectedNodes;
};
int compareNode(node a,node b){
//describe the compare
return a.val2.compare(b.val2); // or any other code
}
template <class T>
class PQueue {
protected:
// this declares a protected member named compareFunction of type pointer to a function which takes 2 T parameters and returns a int. Note that all the parenthesis are mandatory
int (*compareFunction)(T, T);
public:
PQueue (int (*compareFunctionParameter)(T, T)) : compareFunction(compareFunctionParameter) {
// this constructor receives a pointer to function and initializes it's member to that pointer. If the constructor initialization list confuses you, you can read 'compareFunction = compareFunctionParameter '
}
int someMethod() {
// call the function through the pointer you have:
node n1, n2;
n1.val1 = "node1_val1";
n1.val2 = "zzz";
n2.val1 = "node2_val1";
n2.val2 = "aaa";
return compareFunction(n1, n2);
}
};
int main() {
PQueue<node> pq(compareNode);
cout << pq.someMethod() << endl;
return 0;
}
http://ideone.com/EPjbya
Hope this you can use this.
Now to the more versatile example.
C++11 introduces lambdas. http://www.cprogramming.com/c++11/c++11-lambda-closures.html http://www.stroustrup.com/C++11FAQ.html#lambda
#include <iostream>
#include <vector>
#include <string>
#include <functional>
using namespace std;
struct node{
string val1;
string val2;
vector<node *> connectedNodes;
};
int compareNode(node a,node b){
//describe the compare
return a.val2.compare(b.val2); // or any other code
}
template <class T, class Comparator>
class PQueue {
protected:
Comparator compareFunction;
public:
PQueue (Comparator compareFunctionParameter) : compareFunction(compareFunctionParameter) {
}
int someMethod() {
// call the function
node n1, n2;
n1.val1 = "node1_val1";
n1.val2 = "zzz";
n2.val1 = "node2_val1";
n2.val2 = "aaa";
return compareFunction(n1, n2);
}
};
int main() {
// queue with pointer to function
PQueue<node, int (*)(node, node)> pq(compareNode);
cout << pq.someMethod() << endl;
// queue with lamda (anonimous function)
PQueue<node, std::function<int (node, node)>> pq_lambda([](node a, node b) -> int {return a.val1.compare(b.val1);} );
cout << pq_lambda.someMethod() << endl;
return 0;
}
http://ideone.com/ryQmAn You need to compile this code for C++11 standard.
Here the template Comparator can be both pointer to function and lambda. If you are interested in lambdas, the two links I provided above should get you started.