I want to create a multiset of objects of my own Class with a custom comparator, and every example I have seen out there does it without classes.
The code I have so far main.cpp is like this:
multiset<MyClass> myMultiSet;
whereas in Myclass.cpp there is a comparator function:
bool MyClass::operator<(const MyClass c) const {
return (this->data->size() < c.size());
}
Multiset seems to take the less operator by default, and it actually uses the operator< function in the class, but I cannot find a way to specify it to use another operator (such as operator<=)
For example, I would like it to look like this:
multiset<MyClass, MyClass::operator<=> myMultiSet;
bool MyClass::operator<=(const MyClass c) const { // <----- Now it's using this one
return (this->data->size() <= c.size());
}
Member functions are different to functions: they take an the this pointer. As a result, you can't use pointer-to-member-functions directly (and you would need to take the address explicitly as member functions don't decay to pointer-to-member-functions, i.e., &MyClass::operator<=). Also, the parameter is type, not a value. Also, as was pointed out, operator<= doesn't implement a strict weak order, i.e., you can't use that anyway.
The easiest to use an actual operator, whether member of not, is to use the names from <functional>, e.g.
std::multiset<MyClass, std::greater<MyClass>> s;
If the member isn't a known name, I'd think using a lambda is the easiest approach but requires C++20:
std::multiset<MyClass, decltype([](MyClass const&l, MyClass const&r){ return l.member(r); })> s;
Without C++20 I think you can use std::mem_fn although that is slightly annoying as you'll need to provide the pointer to member as a constructor argument:
#include <functional>
#include <set>
struct MyClass
{
bool member(MyClass const&) const { return false; }
};
int main()
{
std::multiset<MyClass, decltype(std::mem_fn(&MyClass::member))> s(std::mem_fn(&MyClass::member));
}
Related
I have a comparison/ordering function that relates to a class. I can use it if I define it as a separate closure object. I would like to make it into a static method of the class it operates on so it is tidier. I guessed how to do this but I get an error that I can't interpret.
Generally I would like to know how to treat static methods as callable objects.
Minimal related example code (not working):
#include <set>
class MyClass {
// More code here
static auto compare(MyClass a, MyClass b) {
return a < b;
}
};
int main() {
std::set<MyClass, decltype(MyClass::compare)> s(MyClass::compare);
return 0;
}
Update, I'm not sure if my example confused the issue, so I updated it.
Couple of issues:
compare is private, make it public.
One must use & to get the address of functions.
#include <set>
class MyClass {
public:
static auto compare(int a, int b) {
return a < b;
}
};
int main() {
std::set<int, decltype(&MyClass::compare)> s(&MyClass::compare);
return 0;
}
Make the function public, and add & in decltype:
std::set<int, decltype(&MyClass::compare)>
I wouldn't consider this to be "tidier" though.
A functor occupies 0 bytes when used as a std::set comparator. But a function pointer (as in your example) occupies 4 or 8 bytes. It also forces you to pass the function to the set's constructor.
Using a function pointer this way only makes sense if you want to switch between different comparators at runtime.
If you do want a pointer, the class itself is unnecessary. You might as well use a free function.
So i have created a class that i use as a hash in an unordered_map
class MyClass
{
hash_type mHash = 0;
hash_type hash() { return mHash; }
bool operator!= (const MyClass& rhs) const;
bool operator== (const MyClass& rhs) const;
}
namespace std
{
template <>
struct hash<MyClass>
{
hash_type operator()(const MyClass& k) const noexcept
{
return k.hash();
}
};
}
It works as expected but i would like to add some functionality.
I would like to be able to use the hash itself when using unordered_map functions like find and erase.
Now i have to do this:
void _erase_key(const MyClass& key) { umap.erase(key); }
But i would like to be able to do this as well:
void _erase_key(const hash_type key) { umap.erase(key); }
Is it possible to somehow use the hash directly instead of the object producing the hash when using methods like find and erase?
If I understand you right, you want to have an std::unordered_map<MyClass, Value> such that you can also query with hash_type and you have hash_type h == MyClass m if std::hash<MyClass>{}(m) == h. Is this correct?
This is not possible in C++17. With C++20, there will be added the functionality of transparent hashes. You can read about that very briefly here. With this, your map has to fulfill certain properties
Your equality type Eq must provide a member type Eq::is_transparent, i.e. you have to put a using is_transparent = some_type; in it. (The exact type is without consequence.)
An object of type Eq has to provide an overload to compare all possible combinations of types you want to use. I.e. provide overloads for (MyClass, MyClass) and (MyClass, hash_type).
Your hash type Hash has to provide a member type Hash::transparent_key_equal, so again, put using transparent_key_equal = some_type; in it.
An object of type Hash must be callable with every type you want to use. I.e. you have to have an operator() overload for both MyClass and hash_type.
For Eq you can use std::equal_to<> (note the empty diamond!) if you provide publically accessible operator== for the appropriate types. This is NOT the default for unordered_map.
There is to the best of my knowledge no analagon for std::hash, so you have to add an own type for that and provide it to the map.
Pre-C++20 the only thing you can do if you want to keep your key type is to write a conversion from hash_type to MyClass.
But I sense a fundamental problem in this, namely that you see two objects of MyClass as identical if they have the same hash. If this is unintentional, you should really fix this. If it is intentional, make sure, that operator==(MyClass, MyClass) also only compares hashes.
In the later case, there is an easy fix for your problem. Change your map to std::unordered_map<hash_type, Value> and hash each MyClass you use to query the map. If you need a reverse lookup to get MyClass from a hash, either write a conversion function from hash_type to MyClass if this is possible, otherwise add another std::unordered_map<hash_type, MyClass>, where you store every object of type MyClass you ever use.
I wrote myself a compare function for sort().
It worked well when I put it this way.
bool comp(string a, string b)
{
...;
}
int main()
{
sort(...,...,comp);
}
However, when I put all this inside a class, say:
class Test {
public:
bool comp(string a,string b)
{
...;
}
vector <string> CustomSort(vector <string> str) {
sort(...,...,comp);
}
};
There is a compile error "No matching function for call to 'sort ......'.
Why would this happen?
Any nonstatic member function of class X has an extra argument - a reference/pointer to (const) X which becomes this. Therefore a member function's signature isn't such that can be digested by sort. You need to use boost::bind or std::mem_fun or std::mem_fun_ref. When using C++11, you can use std::bind.
std::sort(..., ..., std::bind(&Test::comp, this, _1, _2));
Come to think of it, the best solution in this case would be to make your comp function static, because it doesn't need this at all. In this case your original code will work without change.
Read this question about how to compare custom objects in C++.
I did not understand the operator overloading used here. What is the operator being overloaded here? Is it ()? What is the use of overloading that?
struct MyStruct
{
int key;
std::string stringValue;
MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
};
struct less_than_key
{
inline bool operator() (const MyStruct& struct1, const MyStruct& struct2)
{
return (struct1.key < struct2.key);
}
};
The point is to use a less_than_key object as a function:
less_than_key f;
bool result = f(key1, key2);
Object that can be called like functions (including garden variety functions) are sometimes called functors (warning: this is not the meaning of functor in mathematics), or function objects, or sometimes functoids.
Many standard library algorithms and containers expect such function objects as arguments. For instance, std::map can be customized with a binary function object (exactly like the one you showed us) to know how it should sort the keys.
Please refer to this beginners' book list and read about standard algorithms and containers to understand where you can use those beasts.
Function objects and iterators are the core of the C++ standard library, you should really learn about them to make the best use of C++.
The operator() is a function call operator with, probably, the most common use being to allow you to use normal function call syntax on an object that could be considered as behaving in a 'function-like' way.
In this example, you can call less_than_key() which is used by the STL sort.
This is called functor. This is used heavily in C++ apis. Some of these apis take a functor which performs object specific operations.
Example pseudo code:
- Sorting a list of MyStruct objects.
sort api in algorithm.
std::list<MyStruct> mystructlist;
// populate mystructlist
sort(mystructlist.begin(), mystructlist.end(), less_than_key);
It is often called the "functor" operator and it is useful because it mimics the syntax of a call to a regular function.
Objects with such an overloaded operator are often used with stlalgorithms like std::find_if() or in your case, std::sort().
Here are some samples:
std::vector<MyStruct> myvector;
less_than_key comparator;
std::sort(myvector.begin(), myvector.end(), comparator);
Or:
MyStruct a, b;
less_than_key comparator;
if (comparator(a, b))
// Do something
else
// Do something else.
As you can see, there is not syntax difference between calling the operator() method of an instance of less_than_key and calling a function with the same prototype (well, disregarding of the hidden this parameter).
I have a class called Sorter. It has two public items.
int type variable choice
member function called compare with a int type return value that accepts two objects as parameter.
I tried creating an instance of Sorter while passing choice with a value to the constructor,
Then i wanted to use C++ sort function to sort a vector. and to pass the member function compare of the instance i created.
The compare member function uses the variable choice to decide the sorting mechanism.
But i was not able to get the pointer to the member function compare of an instance of Sorter.
Could someone advice me on this?
If you can change the structure of your Sorter class, you could make it a function object by defining operator () like this:
bool Sorter::operator ()(const MyObject &o1, const MyObject &o2) {
// return true if o1 < o2
}
Then you can just pass an instance of your Sorter class to std::sort.
Unfortunately, the standard library is a bit lacking in combinators for things like this. However, boost::lambda can do the job:
#include <boost/lambda/bind.hpp>
namespace l = boost::lambda;
struct foo {
bool bar(char, char);
};
void test(foo *pFoo) {
char a[2] = {0};
std::sort(a, a+1,
l::bind(&foo::bar, pFoo, l::_1, l::_2));
}