Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define generic comparison operator

Tags:

c++

generics

I came up with the idea to define a generic comparison operator which would work with any type, for the fun of it.

#include <cstring>
#include <iostream>

class A
{
    public:
        A(int id) : id(id) {}

    private:
        int id;
};

template <class T>
inline bool operator==(const T& a, const T& b)
{
    return memcmp(&a, &b, sizeof(a)) == 0; // implementation is unimportant (can fail because of padding)
}

int main()
{
    std::cout << (A(10) == A(10)) << std::endl; // 1
    std::cout << (A(10) == A(15)) << std::endl; // 0
}

I think this could be useful to get around the lack of default comparison operator in c++.

Is this a terrible idea? I wonder if doing this could break anything in some circumstances?

like image 514
MaxV37 Avatar asked Aug 09 '17 11:08

MaxV37


4 Answers

Doing this is indeed a terrible idea.

If some type does not define an equality operator, it is most likely because you cannot reasonably compare two objects of that type for equality.

Even for the case where the missing equality operator was an oversight by the implementer, any "catch-all" implementation you would come up with is highly unlikely to do something sensible.

So to conclude: Don't do this! Compile time errors are better than runtime errors; instead of prematurely adding a most certainly broken "solution" hiding the actual problem, add actual solutions as the compile time errors occur.


For starters, the solution you came up with fails for types with padding, types with overloaded unary operator&, and any type that has some pointer or reference like member; or even types with any member or base of any of the aforementioned categories. So for a ton of stuff.

like image 87
Baum mit Augen Avatar answered Nov 15 '22 07:11

Baum mit Augen


Let's take a perfectly normal class, say String. It's implemented as you'd think, with a char* that points to a new[]'ed buffer.

Now compare two of them. Obviously, String("abc")==String("abc"). Yet your implementation fails this test, as the two pointers differ.

Equality is defined by the class semantics, not by the bits directly inside the object.

like image 35
MSalters Avatar answered Nov 15 '22 06:11

MSalters


Yes, it is a terrible idea:
in case of uninitialized pointer:
Here is one failing sample (so this code has two different outputs):

#include <cstring>
#include <iostream>

class A {
public:
  A(int id) : id(id) {}

private:
  int id;
  A* a;
};

template <class T> inline bool operator==(const T &a, const T &b) {
  return memcmp(&a, &b, sizeof(a)) == 0;
}

int main() {
  std::cout << (A(10) == A(10)) << std::endl; // 1
  std::cout << (A(10) == A(15)) << std::endl; // 0
}

output:

0
0

and chances for two same values of RAM initial contents for two pointers is highly unlikely, then the other output is:

1
0
like image 36
wasmup Avatar answered Nov 15 '22 07:11

wasmup


As a slight aside, the nicest way I know of writing equality operators for classes with lots of members uses this idea (this code requires C++ 14):

#include <tuple>

struct foo
{
    int x    = 1;
    double y = 42.0;
    char z   = 'z';

    auto
    members() const
    {
        return std::tie(x, y, z);
    }
};

inline bool
operator==(const foo& lhs, const foo& rhs)
{
    return lhs.members() == rhs.members();
}

int
main()
{
    foo f1;
    foo f2;

    return f1 == f2;
}

Code on Compiler Explorer

like image 43
Jeff Avatar answered Nov 15 '22 06:11

Jeff