correct way to use set_intersection - c++

struct Cord
{
int x_cord;
int y_cord;
Cord(int x = 0,int y = 0):x_cord(x),y_cord(y) {}
bool operator < (const Cord& cord) const
{
if (x_cord == cord.x_cord)
{
return y_cord < cord.y_cord;
}
return x_cord < cord.x_cord;
}
bool operator == (const Cord& cord) const
{
return x_cord == cord.x_cord && y_cord == cord.y_cord;
}
};
std::set<Cord> cordRes,cordTmp;//initialized before
std::set<Cord> cordItersect;
std::set_intersection(cordRes.begin(),cordRes.end(),cordTmp.begin(),cordTmp.end(),cordRes.begin(),cordItersect.begin());
The compilation for above code fails.Any proposiotons how to use set_intersection correctly in the example?
Thanks

Well one problem is that set_intersection only takes five arguments. The other problem is that you need some mechanism to create the elements you are inserting to cordItersect. In your case that would be to use std::inserter.
std::set_intersection(cordRes.begin(), cordRes.end(),
cordTmp.begin(), cordTmp.end(),
std::inserter(cordItersect, cordItersect.end()));

Related

std::set comparator

I'm testing std::set with a custom comparator. But I see the same object getting inserted twice.
Following is the object class:
class Info
{
public:
Info(string n, string oN, int dom):
name(n),
objName(oN),
domain(dom)
{}
void setName(std::string n) { name = n;}
void setObjName(std::string n) { objName = n;}
void setDomain(int dom) { domain = dom; }
std::string getName() const { return name;}
std::string getObjName() const { return objName;}
int getDomain() const { return domain;}
private:
std::string name;
std::string objName;
int domain;
};
Following is my custom comparator:
struct InfoCmp {
bool operator() (const Info &lhs, const Info &rhs) const {
if((lhs.getName() < rhs.getName()) || (lhs.getObjName() < rhs.getObjName()) || (lhs.getDomain() < rhs.getDomain()) ){
return true;
}
return false;
}
};
Following is the usage:
Info rst1("rst1", "rstObj1", 1);
Info rst2("rst2", "rstObj2", 2);
Info rst3("rst1", "rstObj3", 3);
std::set<Info,InfoCmp> resetSet;
resetSet.insert(rst1);
resetSet.insert(rst2);
resetSet.insert(rst3);
resetSet.insert(rst1);
resetSet.insert(rst2);
resetSet.insert(rst3);
I see rst2 inserted twice, but it shouldn't be as per my comparator.
I see that you've come up with your own solution, after recognizing from the comments that your original did not impose a strict object ordering as required by set. Here's a different version that only requires operator< and not operator==, making it consistent with the classes and algorithms of the standard library. It also simplifies things if you're e.g. doing a case insensitive comparison.
struct InfoCmp {
bool operator() (const Info &lhs, const Info &rhs) const {
if(lhs.getName() < rhs.getName())
return true;
if(rhs.getName() < lhs.getName())
return false;
if(lhs.getObjName() < rhs.getObjName())
return true;
if(rhs.getObjName() < lhs.getObjName())
return false;
return lhs.getDomain() < rhs.getDomain();
}
};
struct InfoCmp2 {
bool operator() (const Info &lhs, const Info &rhs) const {
return std::make_tuple(lhs.getName(), lhs.getObjName(), lhs.getDomain()) < std::make_tuple(rhs.getName(), rhs.getObjName(), rhs.getDomain());
}
};
This operator can written with make_tuple as well, and working fine.
as suggested by Cory Kramer, adding strict ordering in comparator fixed the problem.
struct InfoCmp {
bool operator() (const Info &lhs, const Info &rhs) const {
if((lhs.getName() < rhs.getName())
|| ( (lhs.getName() == rhs.getName()) && (lhs.getObjName() < rhs.getObjName()) )
|| ( (lhs.getName() == rhs.getName()) && (lhs.getObjName() == rhs.getObjName()) && (lhs.getDomain() < rhs.getDomain()) )){
return true;
}
return false;
}
};

How to use a custom class as key with std::map if there is no logical way to have a comparison operator defined?

I'm trying to use std::map with a custom class and in the course of the process the program has to call std::map::find in order to resolve a key to a pair. The custom class doesn't seem to fit well in terms of comparisons.
This is probably better explained in code; I have a class that I want to use as a key:
class index_t
{
int vertex_index;
int normal_index;
int texture_index;
}
std::map<index_t, int> reindexer;
I would like to use
reindexer.find(index_to_find);
In order to find a key with exactly same parameters (exactly same vertex/normal/texture indices) exists in the map already.
So technically I want the std::map::find function to behave like this:
bool find(key_to_find) //this is what I'm expecting from a find function of std::map
{
if(existing_key.vertex == key_to_find.vertex && existing_key.texture == key_to_find.texture && existing_key.normal == key_to_find.normal)
return true;
else return false;
}
However, I'm not sure how to overload the comparison operator appropriately in this situation for it to behave like that (since I can think of no logical less than operator that would suit this class). This is the current operator I'm using:
bool operator<(const index_t& rhv)
{
if(vertex_index < rhv && normal_index < rhv && texture_index < rhv)
return true;
else return false;
}
It doesn't work, since the find relies on the function returning "false" reflexively when comparison orders reversed.
How can I get around this?
This is some more specific, compilable code that reproduces the problem:
class index_t
{
public:
int vertex;
int normal;
int texture;
bool operator< (const index_t& rhv) const
{
if (vertex < rhv.vertex && normal < rhv.normal && texture < rhv.texture)
return true;
else return false;
}
};
map<index_t, int> indexMap;
int main()
{
index_t i;
i.vertex = 0;
i.normal = 0;
i.texture = 0;
index_t i2;
i2.vertex = 1;
i2.normal = 0;
i2.texture = 3;
index_t i4;
i4.vertex = 1;
i4.normal = 0;
i4.texture = 3;
index_t i5;
i5.vertex = 6;
i5.normal = 0;
i5.texture = 3;
index_t i8;
i8.vertex = 7;
i8.normal = 5;
i8.texture = 4;
indexMap.insert(pair<index_t, int>(i, 0));
indexMap.insert(pair<index_t, int > (i2, 1));
if (indexMap.find(i5) != indexMap.end())
cout << "found" << endl;
else
cout << "not found" << endl;
system("pause");
return 0;
}
This results in "found" even though i5 is not a part of the map
I also tried this:
class index_t
{
public:
int vertex;
int normal;
int texture;
};
class index_comparator
{
public:
bool operator()(const index_t& lhv, const index_t& rhv) const
{
if (lhv.vertex == rhv.vertex && lhv.normal == rhv.normal && lhv.texture == rhv.texture)
return true;
else return false;
}
};
map<index_t, int, index_comparator> indexMap;
int main()
{
index_t i;
i.vertex = 0;
i.normal = 0;
i.texture = 0;
index_t i2;
i2.vertex = 1;
i2.normal = 0;
i2.texture = 3;
index_t i4;
i4.vertex = 1;
i4.normal = 0;
i4.texture = 3;
index_t i5;
i5.vertex = 6;
i5.normal = 0;
i5.texture = 3;
index_t i8;
i8.vertex = 7;
i8.normal = 5;
i8.texture = 4;
indexMap.insert(pair<index_t, int>(i, 0));
indexMap.insert(pair<index_t, int > (i2, 1));
if (indexMap.find(i5) != indexMap.end())
cout << "found" << endl;
else
cout << "not found" << endl;
system("pause");
return 0;
}
This also results in "found"
The expected results are that when I call std::map::find on a custom class it compares it other keys in the map and only returns true if an exactly same class (containing the same parameters) exists. Otherwise it should return false.
You have to define a strict order to use class index_t as key in a std::map.
It doesn't need to make sense to you – it just has to provide a unique result of less-than for any pairs of index_t instances (and to grant a < b && b < c => a < c).
The (in question) exposed attempt doesn't seem to fulfil this but the following example would:
bool operator<(const index_t &index1, const index_t &index2)
{
if (index1.vertex != index2.vertex) return index1.vertex < index2.vertex;
if (index1.normal != index2.normal) return index1.normal < index2.normal;
return index1.texture < index2.texture;
}
The simplest way to implement the operator is with tuples, it does all the hard work for you:
bool operator<(const index_t& rhv)
{
return std::tie(vertex_index, normal_index, texture_index) < std::tie(rhv.vertex_index, rhv.normal_index, rhv.texture_index);
}
This is equivalent to the required logic:
bool operator<(const index_t& rhv)
{
if (vertex_index != rhv.vertex_index)
{
return vertex_index < rhv.vertex_index;
}
if (normal_index!= rhv.normal_index)
{
return normal_index< rhv.normal_index;
}
return texture_index< rhv.texture_index;
}
In c++20 this gets even easier with the spaceship operator which does everything for you:
auto operator<=>(const index_t&) const = default;
Your ordering doesn't fulfill the requirements, it has to be what is called a "strict weak ordering relation". It's easiest to not implement that yourself, but instead use existing functionality. Examle:
#include <tuple>
bool operator()(const index_t& lhv, const index_t& rhv) const
{
return std::tie(lhv.vertex, lhv.normal, lhv.texture) <
std::tie(rhv.vertex, rhv.normal, rhv.texture);
}
Your comparison function doesn't have to be logical, it just has to impose a strict weak ordering. Here's a version that works.
bool operator<(const index_t& rhv) const
{
if (vertex < rhv.vertex)
return true;
if (vertex > rhv.vertex)
return false;
if (normal < rhv.normal)
return true;
if (normal > rhv.normal)
return false;
if (texture < rhv.texture)
return true;
if (texture > rhv.texture)
return false;
return false;
}
Since this is not a reasonable operator< for your class it would be better to rename it, to avoid confusion.
struct IndexLT
{
bool operator()(const index_t& lhs, const index_t& rhs)
{
// logic as before
}
};
Then use this newly declared functor like this
std::map<index_t, whatever, IndexLT> my_map;
Yet another alternative would be to use a std::unordered_map since ordering doesn't seem to be significant.

Passing set to templatised function

Here's my complete working code
class Point2D
{
protected:
int x, y;
public:
Point2D(int x_, int y_) : x(x_), y(y_)
{
}
int getX() const
{
return x;
}
int getY() const
{
return y;
}
bool operator< (const Point2D & pointObj) const
{
if(x == pointObj.getX() && y == pointObj.getY())
return false;
return pointObj.getX() == x ? true : x < pointObj.getX();
}
};
template<typename T, typename U>
void printSet(const set<T>& setToPrint)
{
set<T, U> pSet;
for(typename set<T>::iterator it = setToPrint.begin(); it != setToPrint.end(); it++)
{
pSet.insert(*it);
}
for(typename set<T,U>::iterator it = pSet.begin(); it != pSet.end(); it++)
{
//here print the element
}
}
int main()
{
set<Point2D> setP2;
setP2.insert(Point2D(1,3));
setP2.insert(Point2D(3,2));
int i = 1;
if(i==1)
{
printSet<Point2D, std::less<Point2D > >(setP2);
i++;
}
if(i==2)
{
printSet<Point2D, std::greater<Point2D > >(setP2);
}
}
It produces a bunch of errors from template code, but I think below is the primary one
/usr/include/c++/5/bits/stl_function.h:377:20: error: no match for ‘operator>’ (operand types are ‘const Point2D’ and ‘const Point2D’)
/usr/include/c++/5/bits/stl_function.h:377:20: note: ‘const Point2D’ is not derived from ‘const std::pair<_T1, _T2>’
What is wrong with the code?
In order to be able to create std::set<Point2D, std::greater<Point2D>>, you need to implement operator> between two objects of type Point2D.
Add a member funtion similar to operator< in the class to resolve the problem.
Suggestion for improvement
The operator< function can be simplified to user fewer compare operations.
bool operator< (const Point2D & pointObj) const
{
if ( x != pointObj.x )
{
return ( x < pointObj.x );
}
return ( y < pointObj.y );
}
The operato> function can be similarly implemented as:
bool operator> (const Point2D & pointObj) const
{
if ( x != pointObj.x )
{
return ( x > pointObj.x );
}
return ( y > pointObj.y );
}
You have implemented the < operator, not the >. C++ is pretty particular about that kind of thing. Implement operator> and you should be all set.
You can do that in terms of operator< if you like:
bool operator> (const Point2D & pointObj) const
{
if(x == pointObj.getX() && y == pointObj.getY())
return false;
return pointObj < this;
}
You can see more examples here.

How to find an element in a vector?

I have defined a structure Coord as
struct Coord {
int x;
int y;
int z;
};
Overloaded operator!= for Coord
bool Coord::operator !=(Coord& crd)const {
if(this->x != crd.x)
{
if(this->y != crd.y)
{
if(this->z != crd.z)
return true;
else
return false;
}
else
return false;
return true;
}
else
return false;
}
Then initialized a vector variable as
vector<Coord> vcoord;
Now I am using following code to get index of vector having a perticular Coord object
int Index::getVIndex(Coord crd) {
vector<Coord>::iterator it;
int indx;
it = vcoord.begin();
while(it != vcoord.end() && (*it) != crd)
++it;
indx = distance(vcoord.begin(),it);
cerr << (*it).x << " " << (*it).y << " " << indx << endl;
return indx;
}
But the value of indx is always 0. Kindly help to get correct result.
You need a not-equals operator for your Coord struct in order to be able to do this:
(*it) != crd
The logic of your not-equals operator is incorrect. The best and easiest option is to provide an equality comparison and use std::find:
struct Coord {
int x;
int y;
int z;
};
bool operator == (const Coord& lhs, const Coord& rhs)
{
return lhs.x==rhs.x && lhs.y==rhs.y && lhs.z==rhs.z;
}
You can then implement != in terms of ==, but you don't need it if you use std::find, which uses == by default:
vector<Coord>::iterator it = std::find(vcoord.begin(), vcoord.end(), crd);
Your != operator returns true only if all coordinates differ; it should return true if any differ. This means your function will return zero if any coordinate of the first element matches the function argument's.
Your version is a long-winded way of writing:
return x != crd.x && y != crd.y && z != crd.z;
when it should be:
return x != crd.x || y != crd.y || z != crd.z;
It may be easier to get the logic correct by implementing it in terms of ==:
bool operator==(Coord const & lhs, Coord const & rhs) {
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
}
bool operator!=(Coord const & lhs, Coord const & rhs) {
return !(lhs == rhs);
}
Also, given a definition of ==, you can use std::find rather than rolling your own loop:
auto found == std::find(vcoord.begin(), vcoord.end(), crd);
if (found == vcoord.end()) {
// Decide what to do if not found.
// Returning zero is a bad idea, since there's no way distinguish that
// from a successful outcome.
} else {
return std::distance(vcoord.begin(), found);
}
You incorrectly implemented the logic in the inequality operator.
It should be
bool Coord::operator !=(const Coord& crd)const {
return x != crd.x || y != crd.y || z != crz.z;
}
Your implementation is logically equivalent to
return x != crd.x && y != crd.y && z != crz.z;

error using c++ set container

Hi all I am a newbie to c++.
After compiling this program, I get an error msg saying .
assign3_3.cpp:120:9: error: could not convert 'sPair' from 'std::set<pairT, clas
scomp>' to 'std::set<pairT>'
here is my code.
#include <set>
#include <string>
#include <iostream>
using namespace std;
struct pairT
{
string first, second;
};
struct classcomp
{
bool operator() (const pairT &lhs, const pairT &rhs) const
{
if (lhs.first == rhs.first && lhs.second == rhs.second)
{
return 0;
}
else if (lhs.first < rhs.first)
{
return -1;
}
else if (lhs.first == rhs.first && lhs.second < rhs.second)
{
return -1;
}
else
{
return 1;
}
}
};
set<pairT> CartesianProduct(set<string> & one, set<string> & two);
int main()
{
string A = "ABC";
string B = "XY";
set<string> sA, sB;
sA.insert(&A[0]);
sA.insert(&A[1]);
sA.insert(&A[2]);
sA.insert(&B[0]);
sA.insert(&B[1]);
set<pairT> pT = CartesianProduct(sA, sB);
//for (set<pairT>::iterator it = pT.begin(); it != pT.end(); it++)
// cout << pT.find(it).first << pT.find(it).second << endl;
return 0;
}
set<pairT> CartesianProduct(set<string> &one, set<string> &two)
{
set<string>::iterator itA, itB;
pairT pT;
set<pairT, classcomp> sPair;
for (itA = one.begin(); itA != one.end(); itA++)
{
//cout << *itA << endl;
for(itB = two.begin(); itB != two.end(); itB++)
{
pT.first = *itA;
pT.second = *itB;
sPair.insert(pT);
}
}
return sPair;
}
First, I am not understanding about the making a comparison function for pairT.
If this is the case here, please explain.
I am having a trouble using set container please help thanks and merry christmas !
The comparator is part of the type. You must say set<pairT, classcomp> everywhere. Best to use a typedef.
In addition to what Kerrek SB said, your comparison function isn't correct.
The comparator required by std::set<std::pair> needs to follow the following logic:
if (lhs.first < rhs.first)
return true;
else if (lhs.first == rhs.first && lhs.second < rhs.second)
return true;
else
return false;
This can be expressed more compactly as:
return lhs.first < rhs.first ||
!(rhs.first < lhs.first) && lhs.second < rhs.second;
Fortunately, this is how std::pair::operator< is defined in the standard library. When you create a std::set<std::pair> this operator will be used by default so you don't have to provide your own.