This question already has answers here:
operator< comparing multiple fields
(6 answers)
Closed 3 years ago.
Consider implementing operator< for the following class:
struct foo {
int a, b;
};
Probably the most common way is something like the element-wise lexicographic compare, like so:
bool operator<(foo lhs, foo rhs) {
return lhs.a < rhs.a || (lhs.a == rhs.a && lhs.b < rhs.b);
}
I've written that enough times, and made a type enough times that I'm wondering if there is something built in to std:: that will do it for me, hopefully with less boilerplate and reasonable code generation.
Now the above for two elements isn't that bad, but the terms multiply as you add more members.
You can use pair<int, int> instead of struct foo.
As pair<type,type> is builtin in std:: it has comparator for all builtin data type. So you don't need any extra operator overloading or a custom comparator. Even it works for strings. std:: compare two pair<type, type> based on the first element of the pair. If it's a tie then the tie is broken based on the second element.
For example-
pair<int,int> a = make_pair(5,10) //declare using make_pair()
pair<int,int> b = {5,7} //different way of declaring pair.
pair<int,int> c = pair<int,int>(7,7) //declare using constructor
Now a < b == false, b < c == true and a < c == true
So in this way, you can use pair<type, type> instead of struct foo. You can replace 'type' using any data type like float, long long, char, string, pair, vector, set, map, complex etc.
Related
This question already has answers here:
How can I use std::maps with user-defined types as key?
(8 answers)
C++ unordered_map using a custom class type as the key
(7 answers)
Closed 3 years ago.
Problem description
I defined a std::map as:
struct Key // Key of my dicitonary
{
float index[3];
};
struct Value // Value of my dictionary
{
float color[3];
float operator[](int &a) const { return color[a]; }
};
Key key = {a, b, c};
Value value = {d, e, f};
std::map<Key, Value> dict = new std::map<Key,Value>;
if (dict.count(key) < 1) { // If key does not exist, add value
addrDict[key] = value;
}
Problem
When I run the code, I get the following error:
d:\documents\mingw\lib\gcc\mingw32\8.2.0\include\c++\bits\stl_function.h:386:20: error: no match for 'operator<' (operand types are 'const Key' and 'const Key')
As far as I understand, it cannot compare two Key object in the map, which is ordered.
For this reason, I need to implement the operator< in the Key struct (even if I don't care about the order of the keys, but using an unordered map returns compiler errors probably related to g++). I just need to keep the keys composed of three different arrays separated. Any idea on how to achieve that?
What I have done
I implemented the operator with a trial and error approach and this solution kind of return what I am expecting, but it does not store all the possible keys:
bool operator<(const Key &rhs) const {
(index[0]*index[1]*index[2]) < (rhs.index[0]* rhs.index[1]* rhs.index[2]);
};
If, on the other hand, I do this:
bool operator<(const Key &rhs) const {
index[0] < rhs.index[0];
};
I only store a few keys, and not all the possible combinations of keys.
EDIT:
solved a problem of variable definition
This question already has answers here:
Sorting a vector of custom objects
(14 answers)
Closed 3 years ago.
In the below C++ snippet,
HOW TO SORT the vector "TwoIntsVec" BASED ON the element "int a" in TwoInts struct. i.e. i need to place the "TwoIntsVec[i] which has the least "TwoIntsVec[i].a" in the 1st place and so on in increasing order of "TwoIntsVec[i].a".
In the below example the vector elemnt struct having 7,3 should be placed 1st as 7 is the least "a" and so on.
struct TwoInts
{
int a;
int b;
};
void PushToVector(int a, int b, std::vector<TwoInts>& TwoIntsVec)
{
TwoInts temp;
temp.a = a;
temp.b = b;
TwoIntsVec.push_back(temp);
}
int main()
{
std::vector<TwoInts> TwoIntsVec;
PushToVector(21,3,TwoIntsVec);
PushToVector(7,3,TwoIntsVec);
PushToVector(12,3,TwoIntsVec);
PushToVector(9,3,TwoIntsVec);
PushToVector(16,3,TwoIntsVec);
// Below sort would NOT work here, as TwoIntsVec is
// not a std::vector<int>
std::sort( TwoIntsVec.begin(), TwoIntsVec.end());
// HOW TO MAKE THE SORT BASED ON the element "int a" in
TwoInts struct
}
You need to pass an appropriate comparison function to std::sort, as there is no appropriate comparison operator available for TwoInts. See overload #3 here with the description of this comparison parameter:
comp - comparison function object (i.e. an object that satisfies the requirements of Compare) which returns ​true if the first argument is less than (i.e. is ordered before) the second. [...]
One C++11 option is to pass a lambda:
std::sort( TwoIntsVec.begin(), TwoIntsVec.end(),
[](const TwoInts& lhs, const TwoInts& rhs){ return lhs.a < rhs.a; });
If you find that this requires too much typing, you can construct a predicate with Boost HOF like this:
#include <boost/hof/proj.hpp>
#include <boost/hof/placeholders.hpp>
using namespace boost::hof;
std::sort(TwoIntsVec.begin(), TwoIntsVec.end(), proj(&TwoInts::a, _ < _));
Or, as a C++20 teaser:
std::ranges::sort(TwoIntsVec, std::less<>{}, &TwoInts::a);
As a side note, I'd recommend you to fill the vector directly via
// Less complicated than doing the same thing in a function:
TwoIntsVec.push_back({21, 3});
TwoIntsVec.push_back({7, 3});
// ...
I want to use a map to count pairs of objects based on member input vectors. If there is a better data structure for this purpose, please tell me.
My program returns a list of int vectors. Each int vector is the output of a comparison between two int vectors ( a pair of int vectors). It is, however, possible, that the output of the comparison differs, though the two int vectors are the same (maybe in different order). I want to store how many different outputs (int vectors) each pair of int vectors has produced.
Assuming that I can access the int vector of my object with .inp()
Two pairs (a1,b1) and (a2,b2) should be considered equal, when (a1.inp() == a2.inp() && b2.inp() == b1.inp()) or (a1.inp() == b2.inp() and b1.inp() == a2.inp()).
This answer says:
The keys in a map a and b are equivalent by definition when neither a
< b nor b < a is true.
class SomeClass
{
vector <int> m_inputs;
public:
//constructor, setter...
vector<int> inp() {return m_inputs};
}
typedef pair < SomeClass, SomeClass > InputsPair;
typedef map < InputsPair, size_t, MyPairComparator > InputsPairCounter;
So the question is, how can I define equivalency of two pairs with a map comparator. I tried to concatenate the two vectors of a pair, but that leads to (010,1) == (01,01), which is not what I want.
struct MyPairComparator
{
bool operator() (const InputsPair & pair1, const InputsPair pair2) const
{
vector<int> itrc1 = pair1.first->inp();
vector<int> itrc2 = pair1.second->inp();
vector<int> itrc3 = pair2.first->inp();
vector<int> itrc4 = pair2.second->inp();
// ?
return itrc1 < itrc3;
}
};
I want to use a map to count pairs of input vectors. If there is a better data structure for this purpose, please tell me.
Using std::unordered_map can be considered instead due to 2 reasons:
if hash implemented properly it could be faster than std::map
you only need to implement hash and operator== instead of operator<, and operator== is trivial in this case
Details on how implement hash for std::vector can be found here. In your case possible solution could be to join both vectors into one, sort it and then use that method to calculate the hash. This is straightforward solution, but can produce to many hash collisions and lead to worse performance. To suggest better alternative would require knowledge of the data used.
As I understand, you want:
struct MyPairComparator
{
bool operator() (const InputsPair& lhs, const InputsPair pair2) const
{
return std::minmax(std::get<0>(lhs), std::get<1>(lhs))
< std::minmax(std::get<0>(rhs), std::get<1>(rhs));
}
};
we order the pair {a, b} so that a < b, then we use regular comparison.
I am using sets. I use a custom struct as the key. I am inserting a value and trying to find the inserted value. But it never seems to find the element.
I have overridden both the == operator and the < operator.
Here is the code of the structure:
struct distance_t
{
public:
int id;
double distance;
bool operator<(const distance_t& rhs) const
{
if(distance < rhs.distance)
return true;
else
return false;
}
bool operator==( const distance_t& rhs)
{
if(id == rhs.id)
return true;
else
return false;
}
};
And this is the code of main
int main()
{
set<distance_t> currentSet;
distance_t insertDistance;
insertDistance.id =1;
insertDistance.distance = 0.5;
currentSet.insert(insertDistance);
distance_t findDistance;
findDistance.id = 1;
assert(currentSet.find(findDistance) != currentSet.end());
}
It always fails in the assert statement. What am I doing wrong?
Edit -Ok now I understand that it does not use the == operator at all. Here is what I want. I need the data structure to be ordered by distance. But I should be able to remove it using the id. Is there any clean way or already existing datastructure to do this?
It fails because your less-than comparison uses distance_t::distance, which you are not setting in findDistance:
distance_t findDistance;
findDistance.id = 1;
std::set does not use operator== for anything. It only uses operator<. So you would have to change it's logic to use distance_t::id.
If you want to search by id without changing the set's ordering, you can use std::find:
set<distance_t>::iterator it = std::find(currentSet.begin(),
currentSet.end(),
findDistance);
This will use your operator==. Bear in mind that this has linear time complexity.
Because operator== is not invoked at all. Comparing elements is like:
!(a < b) && !(b < a)
In other words, it uses operator<.
As you haven't assigned a value to findDistance.distance the result of the less then comparison is undefined.
Note that your definitions of the equality and less then comparison operators is dangerous, because it is easy to define instances of distance_t where their result is inconsistent. One example is two instances with the same distance but different id's.
I am coding in a mixed C/C++ environment. I have an struct in C part and I would like to collect it in a map container in C++ part.
I think I should define a custom key_compare function object, and let STL map::insert() orders nodes. However I don't know how can I modify map container to customize map::find() function. I am looking for a way to customize map::find() function to do something more that key_compare function for equivalence checking.
Would you please let me know how I can put these functions into STL::map or STL::set ?
here is my struct in C part (compile with gcc):
typedef struct iotrace_arh_node
{
double time;
unsigned long long int blkno;
int bcount;
u_int flags;
int devno;
unsigned long stack_no;
} iotrace_arh_node_t;
here is my proposed key_compare and equivalence checking function for find() in the C++ part (compile with g++):
int key_compare ( struct iotrace_arh_node tempa, struct iotrace_arh_node tempb )
{
return (tempa.blkno-tempb.blkno);
}
int key_equal( struct iotrace_arh_node tempa, struct iotrace_arh_node tempb )
{
if( (tempa.blkno == tempb.blkno) && (tempa.bcount == tempb.bcount) )
return 0; // tempa and tempb is equal, node fund in the map
else if ( (tempb.blkno < tempa.blkno) )
return -1; //tempb is less than tempa
else if ( (tempb.blkno >= tempa.blkno) && ( tempb.blkno + tempb.bcount < tempa.blkno + tempa.bcount) )
return 0; // tempa and tempb is equal, node fund in the map
else
return 1; //tempb is grater than tempa
}
To use the type as the key in a map or set, you need to provide a "less-than" comparison, which takes two arguments and returns true if the first should come before the second. The easiest way to use it in a set is to define it as a function object:
struct key_compare {
bool operator()(const iotrace_arh_node & a, const iotrace_arh_node & b) {
return a.blkno < b.blkno;
}
};
and use it as the "comparator" template argument in the map or set:
typedef std::set<iotrace_arh_node, key_compare> node_set;
If you need different ways of comparing the keys, then you can create different sets with different comparators. However, you can't change the comparator once the set is created; the objects in the set are stored according to the order defined by the comparator, so changing it would make the set unusable. If you need to search the same set by different fields, then have a look at Boost.MultiIndex
You don't need to provide an equality comparison.
Standard compare functions differ in C and C++. In C, as you written, you return -1, 0 or 1 when first argument is lesser, equal or greater than the second one. But in C++ you should either overload the < operator, or write a compare function which does the same as < operator and give its name to STL functions. But you should make sure that your < should be transitive (i.e. a<b && b<c => a<c) It means that your key_compare function should be like:
bool key_compare ( const struct iotrace_arh_node& tempa, const struct iotrace_arh_node& tempb )
{
return (tempa.blkno < tempb.blkno);
}
There is no need to define key_equal, because (k1 == k2) <=> (!(k1<k2)&&!(k2<k1)). And AFAIK you can not use different compare functions when you insert and find.
For the comparer, see here: STL Map with custom compare function object
struct my_comparer
{
bool operator() ( const struct iotrace_arh_node& left, const struct iotrace_arh_node& right )
{
return left.blkno < rigth.blkno);
}
}
The comparer must be a binary predicate, not a simple function.
Then you can use it in a Map:
std::map<Key, Data, Compare, Alloc>
(look here: http://www.cplusplus.com/reference/stl/map/ )
Compare and Alloc have default values.
What is your key-type, btw?
hth
Mario