I have a struct with some members and I have an implemented operator== for it. Is it safe to implement the operator< with the help of operator==? I want to use this struct in a set, and I want to check that this struct is unique.
struct Data
{
std::string str1;
std::string str2;
std::string str3;
std::string str4;
bool operator==(const Data& rhs)
{
if (str1 == rhs.str1
&& str2 == rhs.str2
&& str3 == rhs.str3
&& str4 == rhs.str4
)
return true;
else
return false;
}
// Is this ok??
bool operator<(const Data& rhs)
{
return !this->operator==(rhs);
}
}
So when I insert this struct to a std::set what will happen?
Nope, it's quite unsafe. The simplest way to implement it is through std::tie
.
#include <tuple>
struct Data
{
std::string str1;
std::string str2;
std::string str3;
std::string str4;
bool operator<(const Data& rhs) const // you forgot a const
{
return
std::tie(str1, str2, str3, str4) <
std::tie(rhs.str1, rhs.str2, rhs.str3, rhs.str4);
}
}
No, that is not safe. The way you've defined <
, a < b
and b < a
will both be true at the same time.
So when I insert this struct to a std::set what will happen?
The behaviour is undefined, so anything is permitted, and it will likely be different on different implementations.
Well your code suggests that if A!=B
, that means that A<B
which is definitely wrong, since it can as well be A>B
.
You will have to implement your >
and <
operators the same way you did with the operator==
, which means by comparing the objects member-wise. It's up to you to decide how to determine if A
is "more" or "less" than B
based on their members.
If you use the operator as you have it in any of the standard library containers, you will get UB.
You need to define operator<
in its own terms. You cannot implement operator<
in terms of operator==
, although you may be able to do the reverse.
Consider the paradox in this truth table:
"a" < "b" : TRUE
"b" < "a" : TRUE
If your implementation of operator<
yields the above paradox, which it does if you implement it in terms of operator==
then you have not correctly implemented strict weak ordering. What you've implemented is a jumbled mess.
You need to determine which of the member strings take precedence over the others, and then perform a comparison between them in order -- from most important to least important.
For example, if the precedence of the string is, from most important to least important:
str1
str2
str3
str4
...then this yields the following algorithm for operator<
:
bool operator<(const Data& rhs) const
{
if( str1 < rhs.str1 )
return true;
if( rhs.str1 < str1 )
return false;
if( str2 < rhs.str2 )
return true;
if( rhs.str2 < str2 )
return false;
if( str3 < rhs.str3 )
return true;
if( rhs.str3 < str3 )
return false;
if( str4 < rhs.str4 )
return true;
if( rhs.str4 < str4 )
return false;
return false;
}
Using this, you could optionally next re-implement operator==
in terms of operator<
. You assume the inherent inefficiency of time complexity in doing so, however:
bool operator==(const Data& rhs) const
{
return !operator<(rhs) && !rhs.operator<(*this);
}