简体   繁体   中英

Is it safe to use operator != as operator < in a std::set?

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:

  1. str1
  2. str2
  3. str3
  4. 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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM