STL map with custom class comparator not working - c++

I have a map which has a class as key.
In this class I overloaded the < operator, I read here that the map will automatically use that for comparison and sorting.
I get the following error when compiling:
Error 1 error C2664: 'std::pair::pair(const
std::pair &)' : cannot convert argument 1 from
'Position' to 'Position *const &' e:\program files\visual studio 2013
ultimate\vc\include\xmemory0
I don't get what that really means or how I can avoid it. It doesn't highlight anything in my code either.
Here is my code, my class is coordinates, I want them to be sorted from left to right and top to bottom, but the logic isn't the problem here.
There's obviously more stuff in the Position files, but I think these are the relevant parts.
main.cpp
int main()
{
std::map<Position*, int> karte;
Position p1;
p1.setX(0);
p1.setY(0);
Position p2;
p2.setX(0);
p2.setY(1);
Position p3;
p3.setX(1);
p3.setY(0);
Position p4;
p4.setX(1);
p4.setY(1);
karte.emplace(p1, 1);
karte.emplace(p2, 2);
karte.emplace(p3, 3);
karte.emplace(p4, 4);
for (auto& x : karte)
{
std::cout << x.first->toString() << ": " << x.second << std::endl;
}
return 0;
}
Position.h
bool operator<(const Position&) const;
Position.cpp
bool Position::operator<(const Position &position) const
{
if ((x_ < position.x_) && (y_ == position.y_))
{
return true;
}
if (x_ > position.x_ && y_ < position.y_)
{
return true;
}
if (x_ == position.x_ && y_ < position.y_)
{
return true;
}
else
{
return false;
}
}

If you need the map's keys to be pointers, you can create your own comparator and pass it to the map:
struct PositionPointerLesser
{
bool operator() (Position *lhs, Position *rhs) const
{
return *lhs < *rhs; // This calls the operator < in your class
}
};
int main()
{
std::map<Position*, int, PositionPointerLesser> karte;
// ...
}

Redefine your map as:
std::map<Position, int> karte;
The compilation error is because you're trying to insert a pair<Position, int> into the map:
Position p1;
<snip>
karte.emplace(p1, 1);
and you've declared your map as map<Position*, int>

Related

Binary search function with templates, for arrays - compilation problem

To preface my problem: in my project, i have to implement a few functions manually, since i can't use the standard library functions for various reasons.
I have beginner experience with templates so far so some things are still not clear to me.
For this i wanted to implement a simple binary search function:
template<class T, class TValue, ptrdiff_t compare(const T &elem, const TValue &value)>
bool binary_search(T const array[], size_t n, const TValue &value, size_t &index) {
size_t indexLow = 0;
size_t indexHigh = n;
while (indexLow < indexHigh) {
size_t indexMid = indexLow + ((indexHigh - indexLow) / 2);
if (0 > compare(array[indexMid], value)) {
indexLow = indexMid + 1;
continue; //search right
}
indexHigh = indexMid; //search left
}
if ((n > indexLow) && (0 == compare(array[indexLow], value))) {
index = indexLow;
return true;
}
return false;
}
I know it is a bit unorthodox to NOT use iterators or overloaded operators to compare, but i wanted to keep the algorithm itself as simple as possible.
Also it is important for me, that i works with simple arrays (it does not need to work on containers like vector and such).
Using only for size_t, it works perfectly with the following compare function:
ptrdiff_t cmp_size_t(const size_t &elem, const size_t &value) {
if (elem > value)
{
return 1;
}
if (elem < value)
{
return -1;
}
return 0;
}
Example of usage:
size_t test_array0[] = { 1,2,3,4,5,6,7,8 };
size_t n_array0 = sizeof(test_array0) / sizeof(test_array0[0]);
size_t index;
for (size_t search_val = 0; 13 > search_val; ++search_val) {
if (binary_search<size_t, size_t, cmp_size_t>(test_array0, n_array0, search_val, index)) {
std::cout << search_val << " found at index: " << index << std::endl;
}
else {
std::cout << search_val << " not found" << std::endl;
}
}
I would also like to be able to search array of object and array of object pointers too, and this is where i have stumbled into a problem which i have been trying to solve for days now.
Take the following class with a compare function:
struct TestClass {
const void *const value;
TestClass(const void *Value) :value(Value) {}
static ptrdiff_t cmp(const TestClass *&ArrayElem, const void *&SearchValue) {
if (ArrayElem->value > SearchValue) {
return 1;
}
if (ArrayElem->value < SearchValue) {
return -1;
}
return 0;
}
};
If i try to use the same code to search and array:
TestClass testElem0((void *)1);
TestClass testElem1((void *)2);
TestClass testElem2((void *)3);
TestClass *test_array1[] = {
&testElem0,
&testElem1,
&testElem2,
};
size_t n_array1 = sizeof(test_array1) / sizeof(test_array1[0]);
for (size_t search_val = 0; 13 > search_val; ++search_val) {
if (binary_search<TestClass *, void *, TestClass::cmp>(test_array1, n_array1, (void *)search_val, index)) {
std::cout << search_val << " found at index: " << index << std::endl;
}
else {
std::cout << search_val << " not found" << std::endl;
}
}
i get the following compilation error:
1>d:\temp\count_test\count_test\main.cpp(78): error C2672: 'bynary_search': no matching overloaded function found
1>d:\temp\count_test\count_test\main.cpp(78): error C2893: Failed to specialize function template 'bool bynary_search(const T [],std::size_t,const TValue &,size_t &)'
1> d:\temp\count_test\count_test\main.cpp(78): note: With the following template arguments:
1> d:\temp\count_test\count_test\main.cpp(78): note: 'T=TestClass *'
1> d:\temp\count_test\count_test\main.cpp(78): note: 'TValue=void *'
1> d:\temp\count_test\count_test\main.cpp(78): note: 'compare=ptrdiff_t TestClass::cmp(const TestClass *&,const void *&)'
The reason i`m passing by reference is because sometimes i want to search in array of objects, or array of object pointers, and i want to make sure it is not possible to write code, that invokes the copy constructor. I know, that for atomic types like size_t, int, void *, passing by reference is not the best way to do it, but since i'm also passing elements with const, the compiler will know to optimize it.
Also another thing i'm not sure, is if is it possible to deduce some of the template arguments from the function arguments, so i can write:
binary_search<cmp_size_t>(test_array0, n_array0, search_val, index)
instead of
binary_search<size_t, size_t, cmp_size_t>(test_array0, n_array0, search_val, index)
Your template expects a const T& and const TValue&.
When declaring references to pointers the placement of the const matters.
const TestClass *&ArrayElem is a reference to a const pointer.
TestClass* const &ArrayElem is a const-reference to a pointer.
Only the latter will be accepted by your template.

How to overload operator for a set of sets of object

I need to know how to overload the operator = != and < so I could work with set<set<object> >
I have my class:
class pr{
private:
set<set<e> > pr_;
public:
pr();
~pr();
void set_pr(set<e> a);
pr& operator=(const pr& a) const;
bool operator!=(const pr& a) const;
bool operator<(const pr& a) const;
};
So if I have a set like this: {{1,2,3},{4,5}} where the numbers are objects.
I would like to do operations with the sets in other class like this:
void otherclass::myfunction(){
pr prt; //I create the objects
pr prt_old;
set<e> C1; //I create the subset and fill
set<e> C2;
//C1 and C2 filled here
prt.set_pr(C1); //Insert the set<e> into set<set<e>>
prt.set_pr(C2); //It will fail because i dont know how to compare set<e> < <set<e>
while(prt != prt_old){
prt_old = prt ;
prt = create(prt_old);
}
//...
I tried to overload doing this:
pr& pr::operator=(const pr& a) const{
this->clear();
for(set<set<e> >::iterator it =a.begin();it!=a.end();it++){
for(set<e>::iterator j = it->begin(); j != it->end();j++){
this->set_pr(*j);
}
}
return *this;
}
bool pr::operator!=(const pr& a) const{
if(this->size() != a.size()){
return 1;
}
//Now i don't know how to continue for check if for example
// i had {{1,2},{3,4}} and {{1},{2}}
//this two set have same size but they are differnt
//How could i just iterate through the two sets at same time
// and check if subset have too same size or if the objects inside the subset are equal
//Also i need the operator < to insert a set<e> into a set<set<e> > but how??
//Note: class 'e' has already defined the "operator<" for when I insert objects in the set<e>
//And i order them by a function that return an integrer
To test if one set is contained in the other, you iterate over each member of the first set, and test if it exists in the second set.
bool operator<(const pr& a) const {
for (auto _set : _data) {
if (a._data.find(_set) == a._data.end())
return false;
}
return true;
}
To test if two sets are identical, you test their size is equal, and that one is contained in the other
bool operator==(const pr& a) const {
return _data.size() == a._data.size() && *this < a;
}
But notice that there is no need to define an operator==, because the default one defined by std::set is fine.
Here is a full functioning program:
#include <iostream>
#include <set>
using namespace std;
template <class e>
class pr {
private:
set<set<e> > _data;
public:
void insert(set<e> a) { _data.insert(a); }
bool operator==(const pr& a) const {
return _data.size() == a._data.size() && *this < a;
}
bool operator!=(const pr& a) const { return !(*this == a); }
bool operator<(const pr& a) const {
for (auto _set : _data) {
if (a._data.find(_set) == a._data.end())
return false;
}
return true;
}
};
int main()
{
pr<int> a,b,c;
a.insert(set<int>({ 1 }));
b.insert(set<int>({ 1 }));
b.insert(set<int>({ 1, 2 }));
c.insert(set<int>({ 1, 2 }));
c.insert(set<int>({ 1 }));
std::cout << ((a<b) ? "a<b\n" : "NOT a<b\n");
std::cout << ((b<a) ? "b<a\n" : "NOT b<a\n");
std::cout << ((a==c) ? "a==c\n" : "NOT a==c\n");
std::cout << ((b==c) ? "b==c\n" : "NOT b==c\n");
std::cout << ((a==b) ? "a==b\n" : "NOT a==b\n");
return 0;
}

Map and Node, error IntelliSense: no suitable user-defined conversion from "const std::pair<const int, double>" to "Node" exists

I wrote this code for reading and adding polynomials using map and nodes.
The error happens in operator+. I know I have no code which relates my map to Node, I think I should use something in "std::map PolynomialMap"; similar to list but I am not sure what.
Or maybe I should totally change my codes and use another method?
Please let me know how to improve my question if it's not good enough
#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <vector>
#include <map>
using namespace std;
typedef struct Node
{
double cof; // coefficient
int deg; // degree
} Node; // the node of polynomial
class CPolynomial
{
private:
std::map<int, double> PolynomialMap;
public:
CPolynomial();
CPolynomial(const string& file);
virtual ~CPolynomial();
CPolynomial operator+(const CPolynomial &right);
CPolynomial& operator=(const CPolynomial &right);
private:
void AddOneTerm(Node term); // add one term into m_Polynomial
};
int main()
{
CPolynomial p1("P3.txt");
CPolynomial p2("P4.txt");
CPolynomial p3;
p3 = p1 + p2;
return 0;
}
CPolynomial::CPolynomial()
{
;
}
CPolynomial::CPolynomial(const string& file)
{
Node term;
fstream MyFile;
string p;
int num;
MyFile.open(file);
if (!MyFile.is_open())
{
cerr << "Unable to open input file" << endl;
exit(EXIT_FAILURE);
}
else
{
MyFile >> p >> num;
map <int, double>::iterator it = PolynomialMap.begin();
for (int i = 0; i < num; i++)
{
MyFile >> term.deg >> term.cof;
AddOneTerm(term);
}
MyFile.close();
}
}
CPolynomial CPolynomial:: operator+(const CPolynomial &right)
{
CPolynomial temp_polynomial;
temp_polynomial.PolynomialMap = PolynomialMap;
map <int, double> ::const_iterator it; it = right.PolynomialMap.begin();
for (; it != right.PolynomialMap.end(); ++it) //
{
AddOneTerm(*it); //error C2664: 'void CPolynomial::AddOneTerm(Node)' : cannot convert argument 1 from 'const std::pair<const _Kty,_Ty>' to 'Node'
//IntelliSense: no suitable user-defined conversion from "const std::pair<const int, double>" to "Node" exists
}
map <int, double> sum_result = PolynomialMap;
PolynomialMap = temp_polynomial.PolynomialMap;
temp_polynomial.PolynomialMap = sum_result;
sum_result.clear();
return temp_polynomial;
}
CPolynomial& CPolynomial:: operator=(const CPolynomial &right)
{
this->PolynomialMap = right.PolynomialMap;
return *this;
}
void CPolynomial::AddOneTerm(Node term)
{
auto it = PolynomialMap.begin();
while (it != PolynomialMap.end() && it->first < term.deg)
{
++it;
}
if (it != PolynomialMap.end() && term.deg == it->first)
{
it->second += term.cof;
}
else
{
PolynomialMap.insert(pair<int, double>(term.deg, term.cof));
}
}
CPolynomial::~CPolynomial()
{
PolynomialMap.clear();
}
The problem is that you have written your AddOneTerm method to expect an argument that is a Node struct, but in the operator+ method you are attempting to pass it an argument that is a const std::pair<const int, double>.
There are a variety of ways to fix the problem. The following code modifies your code to resolve the problem by providing a constructor in the Node class that can convert a const std::pair<const int, double> into a Node. The addition of a no-argument constructor is also required, since you are using the implicit no-argument constructor of Node when you construct a polynomial by reading a file.
typedef struct Node
{
double cof; // coefficient
int deg; // degree
Node()
{
deg = 0;
cof = 0.0;
}
Node(const pair<const int, double> & nodePair)
{
deg = nodePair.first;
cof = nodePair.second;
}
} Node; // the node of polynomial

STXXL: How to sort Vector of pairs on second element?

Similar Question is available here: How do I sort a vector of pairs based on the second element of the pair? but I am interested in External Memory Sorting.
I have tried using the analogies from Internal Memory Sorting but the error occurs in sorter_stream.h file of STXXL as:
My code :
#include <iostream>
#include <stxxl/vector>
#include <stxxl/sorter>
#include <limits>
using namespace std;
typedef std::pair<int,int> my_pair;
struct my_comparator
{
bool operator()(const my_pair& left, const my_pair& right)
{
return left.first < right.first;
}
int min_value() const
{
return std::numeric_limits<int>::min();
}
int max_value() const
{
return std::numeric_limits<int>::max();
}
};
int main()
{
typedef stxxl::sorter<my_pair, my_comparator> sorter_type;
sorter_type int_sorter(my_comparator(), 64 * 1024 * 1024);
for (int i = 10; i > 0; i--)
{
int_sorter.push(my_pair(i,i+10));
}
int_sorter.sort(); // sort elements (in ascending order)
while (!int_sorter.empty())
{
std::cout << (*int_sorter).first << " "<<(*int_sorter).second<<endl;
++int_sorter;
}
return 0;
}
Error :
sort_stream.h(481): error C2679: binary '=' : no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion)
UPDATE:
Changing the return type of min_value(),max_value() function to my_pair as:
struct my_comparator
{
bool operator()(const my_pair& left, const my_pair& right)
{
return left.first < right.first;
}
my_pair min_value() const
{
return my_pair(std::numeric_limits<int>::min(),std::numeric_limits<int>::min());
}
my_pair max_value() const
{
return my_pair(std::numeric_limits<int>::max(),std::numeric_limits<int>::max());
}
};
gives the following Error:
sort_helper.h(94): error C3848: expression having type 'const my_comparator' would lose some const-volatile qualifiers in order to call 'bool my_comparator::operator ()(const my_pair &,const my_pair &)'
P.S. : Being a novice (Reputation<50) , I am not allowed to comment, that's why writing a new Question.
Got the following example in STXXL:Sorter Section which addresses the same problem.
Code:
#include <stxxl/sorter>
#include <stxxl/stats>
#include <stxxl/timer>
#include <stxxl/random>
#include <limits>
struct TwoInteger
{
int i, j;
TwoInteger()
{ }
TwoInteger(int _i, int _j)
: i(_i), j(_j)
{ }
};
struct TwoIntegerComparator
{
bool operator () (const TwoInteger& a, const TwoInteger& b) const
{
return a.i < b.i;
}
TwoInteger min_value() const
{
return TwoInteger(std::numeric_limits<int>::min(), std::numeric_limits<int>::min());
}
TwoInteger max_value() const
{
return TwoInteger(std::numeric_limits<int>::max(), std::numeric_limits<int>::max());
}
};
int main()
{
// template parameter <ValueType, CompareType, BlockSize(optional), AllocStr(optional)>
typedef stxxl::sorter<TwoInteger, TwoIntegerComparator, 1*1024*1024> sorter_type;
// create sorter object (CompareType(), MainMemoryLimit)
sorter_type int_sorter(TwoIntegerComparator(), 64 * 1024 * 1024);
stxxl::random_number32 rand32;
stxxl::timer Timer1;
Timer1.start();
// insert random numbers from [0,100000)
for (size_t i = 0; i < 1000; ++i)
{
int_sorter.push(TwoInteger(rand32() % 100000, (int)i)); // fill sorter container
}
Timer1.stop();
STXXL_MSG("push time: " << (Timer1.mseconds() / 1000));
stxxl::timer Timer2;
Timer2.start();
int_sorter.sort(); // switch to output state and sort
Timer2.stop();
STXXL_MSG("sort time: " << (Timer2.mseconds() / 1000));
// echo sorted elements
while (!int_sorter.empty())
{
std::cout << int_sorter->i << " "; // access value
++int_sorter;
}
return 0;
}

Subtract one vector from another

I organized two vectors of structures. Now I need to delete what is in chosen from points.
#include <StdAfx.h>;
#include <iostream>;
#include <vector>;
using namespace std;
struct SPoint
{
int id;
int X;
int Y;
};
vector<SPoint> points;
vector<SPoint> chosen;
void print_vect(const vector<SPoint> & vect)
{
for (int i = 0; i < vect.size(); ++i)
{
cout << vect[i].id << " (" << vect[i].X << "," << vect[i].Y << ")"<<endl;
}
cout << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
SPoint temp;
for (int i = 0; i < 10; i++)
{
temp.id = i;
temp.X = i;
temp.Y = i;
points.push_back(temp);
}
for (int i = 5; i < 10; i++)
{
temp.id = i;
temp.X = i;
temp.Y = i;
chosen.push_back(temp);
}
cout << "Points:" << endl;
print_vect(points);
cout << endl << endl;
cout << "Chosen:" << endl;
print_vect(chosen);
system("pause");
return 0;
}
There seems to be set_difference function. But the debugger tells me that I don't have a '<' method. It tells something like this:
error C2784: 'bool std::operator <(const std::move_iterator<_RanIt> &,const std::move_iterator<_RanIt2> &)' : could not deduce template argument for 'const std::move_iterator<_RanIt> &' from 'SPoint
I study procedural programming in C++. And I don't know what to do with this method. And it seems to me that it is impossible to do anything here with "<".
Could you help me execute the subtraction?
Yes, you have guessed correctly. The std::set_difference function needs the < operator to function. It uses it to check equality as (!a
The comparison to check for equivalence of values, uses either
operator< for the first version, or comp for the second, in order to
test this; The value of an element, a, is equivalent to another one,
b, when (!a<b && !b<a) or (!comp(a,b) && !comp(b,a)).
All you would need to do is to add a function like below
bool operator<(const SPoint& p1, const SPoint&p2){
return p1.id <p2.id;
}
Assuming your id field is a unique field. Now you will be able to use the std::set_difference function. This compares two SPoint variables by their id fields.
Note that BOTH ranges need to be sorted for it to work correctly.
You could use e.g. std::remove_if:
std::remove_if(std::begin(points), std::end(points), [](const SPoint& point) {
// Try to find the point in the `chosen` collection
auto result = std::find_if(std::begin(chosen), std::end(chosen),
[](const SPoint& p) {
return (p.id == point.id)
});
// Return `true` if the point was found in `chosen`
return (result != std::end(chosen));
});
Note that I use C++11 lambda functions in the above code.