Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it bad practice for operator== to mutate its operands?

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

  1. 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)?

  2. 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)?

  3. If the answer to either of the above is "yes", then what would be a more appropriate approach?

like image 234
JBentley Avatar asked Apr 12 '13 15:04

JBentley


2 Answers

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.

like image 98
Daniel Frey Avatar answered Oct 23 '22 11:10

Daniel Frey


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.

like image 12
Cheers and hth. - Alf Avatar answered Oct 23 '22 11:10

Cheers and hth. - Alf