vector of pair of set with lambda comparator - c++

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;
}

Related

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.

How to use priority_queue with a non-static compare method of class instance?

Suppose I have a simple class like this:
class Test {
public:
Test(int reference) { m_reference = reference; }
void feed(int x) { m_data.push_back(x); }
int get() { return m_data.front(); }
private:
int m_reference;
std::vector<int> m_data;
};
Instead of a std::vector, I would like to feed values into a std::priority_queue. Instead of returning the .front() value, I would like to .get() the .top() value of the priority_queue based on a custom compare function. Let's say this custom comparison is computed as the absolute difference between a value and the instance reference.
I have no idea how to declare the std::priority_queue in my class attributes.
I have tried:
bool compare(int a, int b) {
return std::abs(a - m_reference) < std::abs(b - m_reference);
}
And then:
std::priority_queue<int, std::vector<int>, decltype(&Test::compare)> m_priority;
I also tried with std::function like this but this raises multiple errors:
std::function<bool(int a, int b)>> pq([this](int a, int b){
return std::abs(a - m_reference) < std::abs(b - m_reference);
});
But this won't work (see Repl.it).
Any idea how to solve this please?
I managed to make it work by using:
std::function<bool(int,int)> comp = [this](int a, int b) { return std::abs(a - m_reference) < std::abs(b - m_reference); };
with
Test(int reference) : m_priority(comp) { m_reference = reference; }
and
std::priority_queue<int, std::vector<int>, decltype(comp)> m_priority;
You also need to #include <functional>
If I understand your question correctly, this is what you wanted?
You can also make your comparator a struct or something and use it instead of std::function if you don't want any performance drawbacks.
Update:
Version with struct would look like this (you can pass a this pointer instead of reference to int or however you prefer it):
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
class Test {
public:
Test(int reference) : m_priority(comp(m_reference)) { m_reference = reference; }
void feed(int x) { m_data.push_back(x); }
int get() { return m_priority.top(); }
struct comp {
int& reference;
comp(int& ref) : reference(ref) {}
bool operator()(int a, int b) { return std::abs(a - reference) < std::abs(b - reference); };
};
private:
int m_reference;
std::vector<int> m_data;
std::priority_queue<int, std::vector<int>, comp> m_priority;
};
If you are fine using std::function (it may have slight overhead) it would work but you tried to submit lambda into type declaration:
std::priority_queue<
int,
std::vector<int>,
std::function<bool(int,int)> comp = [this](int a, int b) { return std::abs(a - m_reference) < std::abs(b - m_reference); }> m_priority;
this would not work. You need to use std::function as a type:
std::priority_queue<
int,
std::vector<int>,
std::function<bool(int,int)>> m_priority;
and then submit lambda into m_priority ctor as parameter:
Test(int reference) :
m_reference( reference ),
m_priority( [ref=reference]( int a, int b ) {
return std::abs( a - ref ) < std::abs( b - ref );
} )
{
}
then it would work. Live example
If you are ever going to change the m_reference value, you would need to re-sort the std::priority_queue. The below is a (probably) clumsy way of doing it, which will be very costly if done often and/or the queue is big, but it gets the job done. The code is meant to be an add-on to #Slavas answer.
public:
void set_reference(int x) {
m_reference = x;
sort();
}
private:
void sort() {
std::priority_queue<int, std::vector<int>, std::function<bool(int,int)>> tmp(
[this](int a, int b) { return std::abs(a - m_reference) < std::abs(b - m_reference); }
);
while(m_priority.size()) {
tmp.emplace(std::move(m_priority.top()));
m_priority.pop();
}
std::swap(tmp, m_priority);
}

std::function from std::bind initialization in the initializer list

I have a map of actions to be taken upon certain choice,
struct option {
int num;
std::string txt;
std::function<void(void)> action;
};
void funct_with_params(int &a, int &b)
{
a = 3; b = 4;
}
int param1 = 1;
int param2 = 3;
I want to initialize vector of those in the new initializer list fashion:
const std::vector<option> choices
{
{
1,
"sometext",
std::bind(&funct_with_params, std::ref(param1), std::ref(param2))
},
}
I can't get the initialization in the vector for function to work, is there a method of passing the std::bind to the vector in some way?
I was able to make the example work by using lambda expression instead of the bind, is there something I am missing? Or is it not the proper way of using std::bind?
I am using C++11 since I'm unable to move to a newer standard.
Problem is that type of action member variable in option is std::function<void(void)> and you are initializing option with different function (std::function<void(int &a, int &b)>). This is due to std::bind functioning (std::bind).
You need correct types. Also i might suggest, because you want to use constant vector, it would be better to use an std::array.
Code sample:
#include <functional>
#include <vector>
#include <array>
struct option {
int num;
std::string txt;
std::function<void(int &a, int &b)> action;
};
void funct_with_params(int &a, int &b){
a = 3; b = 4;
}
int main(){
int param1 = 1;
int param2 = 3;
//vector fill
const std::vector<option> choices{
{ 1, "sometext", std::bind(funct_with_params, std::ref(param1), std::ref(param2)) }
};
//array fill
const std::array<option, 1> choices2 = {
{ 1, "sometext", std::bind(funct_with_params, std::ref(param1), std::ref(param2)) }
};
return 0;
}
Another solution would be using templates.

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++ STL make_heap and pop_heap not working

I need to use a Heap, so i've searched about the STL one, but it doesn't seem to work, i wrote some code to explain what i mean:
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
struct data
{
int indice;
int tamanho;
};
bool comparator2(const data* a, const data* b)
{
return (a->tamanho < b->tamanho);
}
int main()
{
std::vector<data*> mesas;
data x1, x2, x3, x4, x5;
x1.indice = 1;
x1.tamanho = 3;
x2.indice = 2;
x2.tamanho = 5;
x3.indice = 3;
x3.tamanho = 2;
x4.indice = 4;
x4.tamanho = 6;
x5.indice = 5;
x5.tamanho = 4;
mesas.push_back(&x1);
mesas.push_back(&x2);
mesas.push_back(&x3);
mesas.push_back(&x4);
mesas.push_back(&x5);
make_heap(mesas.begin(), mesas.end(), comparator2);
for(int i = 0 ; i < 5 ; i++)
{
data* mesa = mesas.front();
pop_heap(mesas.begin(),mesas.end());
mesas.pop_back();
printf("%d, %d\n", mesa->indice, mesa->tamanho);
}
return 0;
};
and this is what i get:
4, 6
2, 5
1, 3
3, 2
5, 4
So it's not working as a heap, as the maximum element on the vector is not being returned right.
Or am i doing something wrong?
You need to pass comparator2 to std::pop_heap since that is how you created the heap. Otherwise, it will use the default less than operator for pointers, which simply compares the pointer values.
MSN's answer is correct. However, either of a couple style guidelines can prevent this error:
Declare the comparator to work on references, not objects, as operator< would. Use a vector of objects, not pointers.
bool comparator2(const data& a, const data& b)
{
return (a.tamanho < b.tamanho);
}
You might really need the vector of pointers, in which case this doesn't apply.
Use std::priority_queue (from <queue>), which ties together pop_heap and pop_back for you, remembering your comparator. This requires a functor comparator:
struct comparator2 { bool operator()(const data& a, const data& b)
{
return (a.tamanho < b.tamanho);
} };
 
std::priority_queue<data, vector<data>, comparator2> mesas;
// or std::priority_queue<data, vector<data>, comparator2>
mesas.push(x1);
Most elegant way is to make this the default comparison for data:
struct data
{
int indice;
int tamanho;
 
friend bool operator<(const data& a, const data& b)
{
return (a.tamanho < b.tamanho);
}
};
std::priority_queue<data> mesas;
mesas.push(x1);
priority_queue can also take a prefilled unsorted container, which it will copy.
What about std::set
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include <set>
struct data
{
// Always put constructors on.
// When the constructor is finished the object is ready to be used.
data(int i,int t)
:indice(i)
,tamanho(t)
{}
int indice;
int tamanho;
// Add the comparator to the class.
// Then you know where to look for it.
bool operator<(data const& b) const
{
return (tamanho < b.tamanho);
}
};
int main()
{
std::set<data> mesas;
// Dont declare all your variables on the same line.
// One per line otherwise it is hard to read.
data x1(1,3);
data x2(2,5);
data x3(3,2);
data x4(4,6);
data x5(5,4);
mesas.insert(x1);
mesas.insert(x2);
mesas.insert(x3);
mesas.insert(x4);
mesas.insert(x5);
// You don't actually need the variables.
// You could have done it in place.
mesas.insert(data(6,100));
// Use iterator to loop over containers.
for(std::set<data>::iterator loop = mesas.begin(); loop != mesas.end(); ++loop)
{
printf("%d, %d\n", loop->indice, loop->tamanho);
}
return 0;
};
I had a similar problem and was able to solve it with something like this:
struct comparator2 { bool operator()(data const * const a, data const * const b)
{
return (a->tamanho < b->tamanho);
} };
std::priority_queue<data*, std::vector<data*>, comparator2> mesas;