I want to declare a multiset of a structure.
The current form of my declaration of multiset is
struct obj
{
//code
};
struct compare
{
inline bool operator()(const obj &a, const obj &b)
{
//code
}
};
multiset<obj,compare> mst;
Is there any other form of declaration which may use only one structre by overloading operator for obj.
Yes, you can overload operator < for your struct, either as a member function or as a free-standing one. This will make code more concise (in my purely subjective opinion), but in practice would not lead to any difference in performance.
As a matter of fact, optimizing compiler is more than likely to generate exactly the same code (provided the actual comparison code is the same).
Related
While doing some unit tests I want to be able to compare some pretty simple structs (they contain only public data members). I could write a operator== for all of them separately but it would be cumbersome and repetitive. So I decided to try to do this in a generic way. Yet there is a problem - some of them are not POD as some of their fields have non-POD type, let's say a std::list for an example.
struct NonPod {
std::list<int> lst;
};
struct NonPod2 {
std::list<NonPod> lst;
};
template<class T>
bool operator==(const T& lhs, const T& rhs) {
//what should I put here to make it work safely
//to compare NonPod with other NonPod
//ant NonPod2 with other NonPod2
}
AFAIK, to compare POD safely I could just use std::memcmp and it would be all fine. Is it possible to do generic operator== for non-POD types as well? If so, how?
Sadly, there is no way to do this in C++17 (or earlier). C++20 will allow you to add:
auto operator<=>(const class_name&) const = default;
to each class. This will give you all the comparison operators defined in the obvious way.
If the classes are being created by a code generator, then adding a comparison function should be easy.
There is a popular idiom using std::tie to implement comparison operators:
// foo.h
struct Foo {
int a, b;
string c;
bool operator<(const Foo& rhs) const;
};
// foo.cc
bool Foo::operator<(const Foo& rhs) const {
return tie(a, b, c) < tie(rhs.a, rhs.b, rhs.c);
}
E.g. it's widely used in Chromium
However it requires copying the list of members, so why not write a helper function:
static auto MakeTie(const Foo& x) {
return tie(x.a, x.b, x.c);
}
bool Foo::operator<(const Foo& rhs) const {
return MakeTie(*this) < MakeTie(rhs);
}
// or, in foo.h
auto MakeTie() const;
// and in foo.cc
auto Foo::MakeTie() const { ... }
(btw such member function cannot be called from any other translation unit)
So, why do I see hundreds of such tie(a, b, c) < tie(copy-pasta) pairs, is there a reason behind this?
Firstly, if your class has so many members that doubling up tie is problematic, then you may have a design smell anyway.
I would tend to agree that this is a bit of an annoyance, but remember that it's not the reason for tie's being. There is no such thing as "a tie"; "tie" here is a verb, a way to describe how expressions are being "tied together" into what is actually a tuple of references.
You can of course write your own replacement for tie that knows how all the relevant members of your class, so that this does not need writing out twice. You could call it members_as_tuple. It's up to you as to whether you want to do that, just as it is up to you whether to make any function to avoid some particular duplicated code.
Certainly, though, without reflection, C++ cannot do this for you in the general case, so that's why such a facility isn't provided out of the box.
tl;dr: You've already shown the best (only?) way to do it, but I wouldn't call it make_tie.
As for why people aren't doing this more, well, that's unanswerable. They probably just didn't think of it, or didn't think they needed it, and were probably right.
at first I'm new here and English isn't my native language so apologize for any grammatical failures but I find this community really nice so I will try to ask my question as precise as I can.
I want to add my own class object into a stl container multiset and want to sort it with my own overloaded less operator defined in my class. I really tried out several solutions but nothing really worked so I hope someone can give me some useful hints to solve it.
Here is my general idea of my class definition:
class object {
public:
int first;
string second;
object(int f, string s) {
first = f;
second = s;
}
bool operator<(const object &comp) {
return first < comp.first;
}
};
This was my first try and it didn't work so I also tried out to declare the overloaded operator as a friend method but it didn't work also.
Here is a short code extract from my main function:
includes ...
//code omitted
int main() {
multiset<object*> mmset;
mmset.insert(new object(10, "test"));
mmset.insert(new object(11, "test"));
return 0;
}
After a while I started to debugging my code and try to figure out where the problem is and I come across the following thing that have made me a bit suspicious.
code extract from the stl:
// TEMPLATE STRUCT less
template<class _Ty>
struct less : public binary_function<_Ty, _Ty, bool>
{ // functor for operator<
bool operator()(const _Ty& _Left, const _Ty& _Right) const
{ // apply operator< to operands
return (_Left < _Right);
}
};
I have set a breakpoint on this line and observed what the program is doing here and I don't know why, but it only compares the addresses from the two objects and return so always false. It never calls my overloaded less operator although the operator exists and the _Left and _Right variables contain the address to my object.
I would really appreciate it if someone could help me.
Best Greetings
Tom
You are not storing objects in your multiset. You are storing object*s. These are pointers to objects. This means the set will order the pointers that you're inserting into it.
It seems like you really just want a multiset<object>:
multiset<object> mmset;
mmset.emplace(10, "test");
mmset.emplace(11, "test");
Now it will use < to compare the objects themselves.
If you really want to store pointers, you'll need to provide a custom comparator to the multiset. In C++11, you can do this easily with a lambda:
auto f = [](int* a, int* b) { return *a < *b; };
std::multiset<int*, decltype(f)> mmset(f);
Pre-C++11, you can create a function object that implements operator() with the same body as this lambda function.
Thank you for your help. That's seems to be a good solution to solve this problem.
I have searched a bit deeper in the new C++11 standard and found out that there is another possible solution to solve this with a little bit simpler implementation but the same result :)
I will post it just as information for other seekers with the same problem.
You can pass any constructor a stl container a so-called comparison object which the container will use to arrange your content.
The only thing you have to do is to define the overloaded operator() in your class and "misuse" them as a comparison operator.
class object {
int first;
string second;
object() { };
object(int f, string s) {
first = f;
second = s;
}
bool operator()(const object *comp1, const object *comp2) const {
return comp1->first < comp2->first;
}
}
The other thing what you have additionally to do now is to pass the object as the second argument in your definition of the container:
multiset(object*, object) mmset;
You can also use an extra class for this purpose just for comparison because otherwise you need a default constructor to use this class in this way.
Scenario
I have a class which I want to be able to compare for equality. The class is large (it contains a bitmap image) and I will be comparing it multiple times, so for efficiency I'm hashing the data and only doing a full equality check if the hashes match. Furthermore, I will only be comparing a small subset of my objects, so I'm only calculating the hash the first time an equality check is done, then using the stored value for subsequent calls.
Example
class Foo
{
public:
Foo(int data) : fooData(data), notHashed(true) {}
private:
void calculateHash()
{
hash = 0; // Replace with hashing algorithm
notHashed = false;
}
int getHash()
{
if (notHashed) calculateHash();
return hash;
}
inline friend bool operator==(Foo& lhs, Foo& rhs)
{
if (lhs.getHash() == rhs.getHash())
{
return (lhs.fooData == rhs.fooData);
}
else return false;
}
int fooData;
int hash;
bool notHashed;
};
Background
According to the guidance on this answer, the canonical form of the equality operator is:
inline bool operator==(const X& lhs, const X& rhs);
Furthermore, the following general advice for operator overloading is given:
Always stick to the operator’s well-known semantics.
Questions
My function must be able to mutate it's operands in order to perform the hashing, so I have had to make them non-const. Are there any potential negative consequences of this (examples might be standard library functions or STL containers which will expect operator== to have const operands)?
Should a mutating operator== function be considered contrary to its well-known semantics, if the mutation doesn't have any observable effects (because there's no way for the user to see the contents of the hash)?
If the answer to either of the above is "yes", then what would be a more appropriate approach?
It seems like a perfectly valid usecase for a mutable member. You can (and should) still make your operator== take the parameters by const reference and give the class a mutable member for the hash value.
Your class would then have a getter for the hash value that is itself marked as a const method and that lazy-evaluates the hash value when called for the first time. It's actually a good example of why mutable was added to the language as it does not change the object from a user's perspective, it's only an implementation detail for caching the value of a costly operation internally.
Use mutable for the data that you want to cache but which does not affect the public interface.
U now, “mutate” → mutable.
Then think in terms of logical const-ness, what guarantees the object offers to the using code.
You should never modify the object on comparison. However, this function does not logically modify the object. Simple solution: make hash mutable, as computing the hash is a form of cashing. See:
Does the 'mutable' keyword have any purpose other than allowing the variable to be modified by a const function?
Having side effect in the comparison function or operator is not recommended. It will be better if you can manage to compute the hash as part of the initialization of the class. Another option is to have a manager class that is responsible for that. Note: that even what seems as innocent mutation will require locking in multithreaded application.
Also I will recommend to avoid using the equality operator for classes where the data structure is not absolutely trivial. Very often the progress of the project creates a need for comparison policy (arguments) and the interface of the equality operator becomes insufficient. In this case adding compare method or functor will not need to reflect the standard operator== interface for immutability of the arguments.
If 1. and 2. seem overkill for your case you could use the c++ keyword mutable for the hash value member. This will allow you to modify it even from a const class method or const declared variable
Yes, introducing semantically unexpected side-effects is always a bad idea. Apart from the other reasons mentioned: always assume that any code you write will forever only be used by other people who haven't even heard of your name, and then consider your design choices from that angle.
When someone using your code library finds out his application is slow, and tries to optimize it, he will waste ages trying to find the performance leak if it is inside an == overload, since he doesn't expect it, from a semantic point of view, to do more than a simple object comparison. Hiding potentially costly operations within semantically cheap operations is a bad form of code obfuscation.
You can go the mutable route, but I'm not sure if that is needed. You can do a local cache when needed without having to use mutable. For example:
#include <iostream>
#include <functional> //for hash
using namespace std;
template<typename ReturnType>
class HashCompare{
public:
ReturnType getHash()const{
static bool isHashed = false;
static ReturnType cachedHashValue = ReturnType();
if(!isHashed){
isHashed = true;
cachedHashValue = calculate();
}
return cachedHashValue;
}
protected:
//derived class should implement this but use this.getHash()
virtual ReturnType calculate()const = 0;
};
class ReadOnlyString: public HashCompare<size_t>{
private:
const std::string& s;
public:
ReadOnlyString(const char * s):s(s){};
ReadOnlyString(const std::string& s): s(s){}
bool equals(const ReadOnlyString& str)const{
return getHash() == str.getHash();
}
protected:
size_t calculate()const{
std::cout << "in hash calculate " << endl;
std::hash<std::string> str_hash;
return str_hash(this->s);
}
};
bool operator==(const ReadOnlyString& lhs, const ReadOnlyString& rhs){ return lhs.equals(rhs); }
int main(){
ReadOnlyString str = "test";
ReadOnlyString str2 = "TEST";
cout << (str == str2) << endl;
cout << (str == str2) << endl;
}
Output:
in hash calculate
1
1
Can you give me a good reason to keep as to why keeping isHashed as a member variable is necessary instead of making it local to where its needed? Note that we can further get away from 'static' usage if we really want, all we have todo is make a dedicated structure/class
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).