64

I am trying to create an example, which would check the existence of the operator== (member or, non-member function). To check whether a class has a member operator== is easy, but how to check whether it has a non-member operator==?

This is what I have to far :

#include <iostream>

struct A
{
    int  a;

    #if 0
    bool operator==( const A& rhs ) const
    {
        return ( a==rhs.a);
    }
    #endif
};
#if 1
bool operator==( const A &l,const A &r )
{
    return ( l.a==r.a);
}
#endif


template < typename T >
struct opEqualExists
{
    struct yes{ char a[1]; };
    struct no { char a[2]; };

    template <typename C> static yes test( typeof(&C::operator==) );
    //template <typename C> static yes test( ???? );
    template <typename C> static no test(...);

    enum { value = (sizeof(test<T>(0)) == sizeof(yes)) };
};

int main()
{
    std::cout<<(int)opEqualExists<A>::value<<std::endl;
}

Is it possible to write a test function to test the existence of non-member operator==? If yes, how?

btw I have checked similar questions, but haven't found a proper solution :
Is it possible to use SFINAE/templates to check if an operator exists?

This is what I tried :

template <typename C> static yes test( const C*,bool(*)(const C&,constC&) = &operator== );

but the compilation fails if the non-member operator== is removed

Community
  • 1
  • 1
BЈовић
  • 62,405
  • 41
  • 173
  • 273

14 Answers14

48

C++03

The following trick works and it can be used for all such operators:

namespace CHECK
{
  class No { bool b[2]; };
  template<typename T, typename Arg> No operator== (const T&, const Arg&);

  bool Check (...);
  No& Check (const No&);

  template <typename T, typename Arg = T>
  struct EqualExists
  {
    enum { value = (sizeof(Check(*(T*)(0) == *(Arg*)(0))) != sizeof(No)) };
  };  
}

Usage:

CHECK::EqualExists<A>::value;

The 2nd template typename Arg is useful for some special cases like A::operator==(short), where it's not similar to class itself. In such cases the usage is:

CHECK::EqualExists<A, short>::value
//                    ^^^^^ argument of `operator==`

Demo.


C++11

We need not use sizeof and null reference trick when we have decltype and std::declval

namespace CHECK
{
  struct No {}; 
  template<typename T, typename Arg> No operator== (const T&, const Arg&);

  template<typename T, typename Arg = T>
  struct EqualExists
  {
    enum { value = !std::is_same<decltype(std::declval<T>() < std::declval<Arg>()), No>::value };
  };  
}

Demo

Marc Dirven
  • 309
  • 2
  • 18
iammilind
  • 68,093
  • 33
  • 169
  • 336
  • 1
    As `sizeof(bool)` is implementation defined, there could be a platform, where this equals to `7`. I would suggest using `typedef bool no[2]` instead. – Thomas B. Aug 22 '14 at 10:07
  • @ThomasB, There cannot be a platform with `sizeof(char) = 7` **practically**. `bool[2]` is usually equivalent to `char[2]`. – iammilind Aug 23 '14 at 14:15
  • 5
    `sizeof(char)` is always 1, no doubt. But since you want to distinguish your `operator==` from a normal one that returns a `bool` by comparing the size of the return type, you need to compare something against the `sizeof(bool)`. But [`sizeof(bool)` is implementation-defined](http://stackoverflow.com/questions/4897844/is-sizeofbool-defined) and hence you don't know if seven chars are the same as 1 bool, or anything else. You only know that 2 bools occupy more space than one: `sizeof(bool) != sizeof(bool[2])`. But yeah, that's only nit-picking ;) – Thomas B. Aug 23 '14 at 15:48
  • What about: `typedef char no[sizeof(bool)+1];` – pts Feb 16 '16 at 00:53
  • @pts it may work but not required. Why do you want `sizeof bool`, when you can simply write any number greater than 1 manually. Also you assume here the sizes of `bool` and `char` to be same. I also think of it as always correct in most architectures, but not sure. – iammilind Feb 16 '16 at 02:04
  • @iammilind: To make it work everywhere, we need `sizeof(no)` be different from `sizeof(bool)`. My solution (`typedef char no[sizeof(bool)+1];`) guarantees that, while `typedef char no[7];` doesn't (because the C++ standard permits architectures where `sizeof(bool) == 7`). – pts Feb 18 '16 at 14:56
  • 1
    What if user-provided `==` returns X such that `sizeof(X) == sizeof(no)`? X could be contextually convertible to `bool`. [See this example](http://ideone.com/rTIA3q). – Nawaz Apr 01 '16 at 10:26
  • @iammilind: I've submitted my solution. Please have a look at it. – Nawaz Apr 01 '16 at 15:53
  • @iammilind: Even with C++03, the *limitation* can be removed, with one more indirection: http://coliru.stacked-crooked.com/a/17c3fcd34b51e217 .. Now it does NOT depend on any magic size of the return type of `==`. – Nawaz Apr 02 '16 at 04:28
  • @Nawaz, it's indeed true that the thought sparks in bulk to multiple people. Just around an hour back while outside, I was remembering my old answers where I had used the similar trick of return types. I tested successfully in the compiler and when opened SO, I saw that coincidentally you also put it as a comment. Yes, I have edited the answer now. Instead of using `template` again, I have chosen to use simple `...` operator for a change. Hope that doesn't have any corner case :-) BTW you are comparing sizes of `int` & `char`, which will work mostly; But to be pedantic, it's good to use array. – iammilind Apr 02 '16 at 05:02
  • If `int` is not allowed to be `1` byte (by the spec), then `int` is as good (along with`char` as counter-part) as an array of some type like you have done it. – Nawaz Apr 02 '16 at 08:26
  • C++11 can use `static constexpr bool` instead of `enum`. – Nick Jul 04 '16 at 11:17
  • Pretty cool C++11 solution. How would you extend it to member-only operator overloads e.g `[]`?? – joaocandre Nov 13 '18 at 17:31
  • @joaocandre, that will be different topic, hence I encourage you to ask a separate question, while referencing this post in it. You may mention the requirement and expectation clearer. – iammilind Nov 15 '18 at 02:02
  • 2
    You have written "<" instead of "==" in C++11 version :-) – Olle Lindeberg Jan 18 '22 at 20:59
16

Have a look at Boost's Concept Check Library (BCCL) http://www.boost.org/doc/libs/1_46_1/libs/concept_check/concept_check.htm.

It enables you to write requirements that a class must match in order for the program to compile. You're relatively free with what you can check. For example, verifying the presence of operator== of a class Foo would write as follow:

#include <boost/concept_check.hpp>


template <class T>
struct opEqualExists;

class Foo {
public:
    bool operator==(const Foo& f) {
       return true;
    }

   bool operator!=(const Foo& f) {
      return !(*this == f);
   }

   // friend bool operator==(const Foo&, const Foo&);
   // friend bool operator!=(const Foo&, const Foo&);
};

template <class T>
struct opEqualExists {
   T a;
   T b;

   // concept requirements  
   BOOST_CONCEPT_USAGE(opEqualExists) {
      a == b;
   }
};


/*
bool operator==(const Foo& a, const Foo& b) {
   return true; // or whatever
}
*/


/*
bool operator!=(const Foo& a, const Foo& b) {
   return ! (a == b); // or whatever
}
*/


int main() {
   // no need to declare foo for interface to be checked

   // declare that class Foo models the opEqualExists concept
   //   BOOST_CONCEPT_ASSERT((opEqualExists<Foo>));
   BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Foo>)); // need operator!= too
}

This code compiles fine as long as one of the two implementations of operator== is available.

Following @Matthieu M. and @Luc Touraille advice, I updated the code snippet to provide an example of boost::EqualityComparable usage. Once again, please note that EqualityComparable forces you to declare operator!= too.

jopasserat
  • 5,721
  • 4
  • 31
  • 50
  • 1
    I think one of the guidelines of Boost.Concept is to avoid creating local variables (`T` is not necessarily default constructible) and instead put them as member variables of the checker. – Matthieu M. Jun 30 '11 at 12:58
  • 3
    I think the Boost Concept Checking Library already defined a set of "standard" concepts such as [EqualityComparable](http://www.sgi.com/tech/stl/EqualityComparable.html) (cf. [the reference](http://www.boost.org/doc/libs/1_46_1/libs/concept_check/reference.htm)). Moreover, your concept not only requires a comparison operator, but also a default constructor. – Luc Touraille Jun 30 '11 at 13:03
  • 1
    @Luc Touraille: I agree with the use of EqualityComparable but please note that it forces you to have `operator!=` defined either. – jopasserat Jun 30 '11 at 13:21
15

It's also possible to use only c++11 type traits to check the existence of the member:

#include <type_traits>
#include <utility>

template<class T, class EqualTo>
struct has_operator_equal_impl
{
    template<class U, class V>
    static auto test(U*) -> decltype(std::declval<U>() == std::declval<V>());
    template<typename, typename>
    static auto test(...) -> std::false_type;

    using type = typename std::is_same<bool, decltype(test<T, EqualTo>(0))>::type;
};

template<class T, class EqualTo = T>
struct has_operator_equal : has_operator_equal_impl<T, EqualTo>::type {};

You can use the trait like so:

bool test = has_operator_equal<MyClass>::value;

The resulting type of has_operator_equal will either be std::true_type or std::false_type (because it inherits from an alias of std::is_same::type), and both define a static value member which is a boolean.


If you want to be able to test whether your class defines operator==(someOtherType), you can set the second template argument:

bool test = has_operator_equal<MyClass, long>::value;

where the template parameter MyClass is still the class that you are testing for the presence of operator==, and long is the type you want to be able to compare to, e.g. to test that MyClass has operator==(long).

if EqualTo (like it was in the first example) is left unspecified, it will default to T, result in the normal definition of operator==(MyClass).

Note of caution: This trait in the case of operator==(long) will be true for long, or any value implicitly convertible to long, e.g. double, int, etc.


You can also define checks for other operators and functions, just by replacing what's inside the decltype. To check for !=, simply replace

static auto test(U*) -> decltype(std::declval<U>() == std::declval<V>());

with

static auto test(U*) -> decltype(std::declval<U>() != std::declval<V>());
Nicolas Holthaus
  • 7,763
  • 4
  • 42
  • 97
  • Where is the variable named `value` in the code? It will be good to have an online demo with minimal code. BTW, I have edited my answer today to be able to allow the other versions of `operator==`, such as `MyClass::operator=(long)`. You may want to incorporate those changes as well. – iammilind Apr 01 '16 at 17:36
  • @iammilind `has_operator_equal` inherits from `std::is_same::type`, meaning it's going to be one of either a `std::true_type`, or `std::false_type`, both of which define a `value` static boolean member. This is idiomatic for c++11 type traits. I'll update to add the right-hand-side type. – Nicolas Holthaus Apr 01 '16 at 17:57
  • 2
    I would modify this to check that `!= false_type` because `==` might return something other than `bool` while still having the same semantic meaning. This includes `false_type` in fact, so I'd think about using something totally unique. – Edward Strange May 19 '17 at 23:20
  • @EdwardStrange I think for most purposes the result has to be convertible to bool though or else most usages like `if ( a == b )` will fail anyway. So, testing with `is_convertible` instead of `is_same` should be sufficient. – mxmlnkn Aug 28 '22 at 13:32
12

C++20

I guess you want to check whether a user-provided type has equality operator or not; if that is the case then Concepts are here to help.

#include <concepts>

struct S{
   int x;
};

template<std::equality_comparable T>
bool do_magic(T a, T b)
{
    return a == b;
}

int main()
{
    // do_magic(S{}, S{}); Compile time error
    do_magic(56, 46); // Okay: int has == and !=
}

If you pass any type that does not have == and != defined, the compiler just errors out with message, e.g.:

equality_comparable concept not satisfied by type


You can also use std::equality_comparable_with<T, U> concept to check for those overload between two different types.

There are many more concepts that have been added to standards such as std::incrementable etc.. Have a look at Standard Library concepts as a good starting point.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
coder3101
  • 3,920
  • 3
  • 24
  • 28
10

As of c++14, the standard binary functions do most of the work for us for the majority of operators.

#include <utility>
#include <iostream>
#include <string>
#include <algorithm>
#include <cassert>


template<class X, class Y, class Op>
struct op_valid_impl
{
    template<class U, class L, class R>
    static auto test(int) -> decltype(std::declval<U>()(std::declval<L>(), std::declval<R>()),
                                      void(), std::true_type());

    template<class U, class L, class R>
    static auto test(...) -> std::false_type;

    using type = decltype(test<Op, X, Y>(0));

};

template<class X, class Y, class Op> using op_valid = typename op_valid_impl<X, Y, Op>::type;

namespace notstd {

    struct left_shift {

        template <class L, class R>
        constexpr auto operator()(L&& l, R&& r) const
        noexcept(noexcept(std::forward<L>(l) << std::forward<R>(r)))
        -> decltype(std::forward<L>(l) << std::forward<R>(r))
        {
            return std::forward<L>(l) << std::forward<R>(r);
        }
    };

    struct right_shift {

        template <class L, class R>
        constexpr auto operator()(L&& l, R&& r) const
        noexcept(noexcept(std::forward<L>(l) >> std::forward<R>(r)))
        -> decltype(std::forward<L>(l) >> std::forward<R>(r))
        {
            return std::forward<L>(l) >> std::forward<R>(r);
        }
    };

}

template<class X, class Y> using has_equality = op_valid<X, Y, std::equal_to<>>;
template<class X, class Y> using has_inequality = op_valid<X, Y, std::not_equal_to<>>;
template<class X, class Y> using has_less_than = op_valid<X, Y, std::less<>>;
template<class X, class Y> using has_less_equal = op_valid<X, Y, std::less_equal<>>;
template<class X, class Y> using has_greater_than = op_valid<X, Y, std::greater<>>;
template<class X, class Y> using has_greater_equal = op_valid<X, Y, std::greater_equal<>>;
template<class X, class Y> using has_bit_xor = op_valid<X, Y, std::bit_xor<>>;
template<class X, class Y> using has_bit_or = op_valid<X, Y, std::bit_or<>>;
template<class X, class Y> using has_left_shift = op_valid<X, Y, notstd::left_shift>;
template<class X, class Y> using has_right_shift = op_valid<X, Y, notstd::right_shift>;

int main()
{
    assert(( has_equality<int, int>() ));
    assert((not has_equality<std::string&, int const&>()()));
    assert((has_equality<std::string&, std::string const&>()()));
    assert(( has_inequality<int, int>() ));
    assert(( has_less_than<int, int>() ));
    assert(( has_greater_than<int, int>() ));
    assert(( has_left_shift<std::ostream&, int>() ));
    assert(( has_left_shift<std::ostream&, int&>() ));
    assert(( has_left_shift<std::ostream&, int const&>() ));

    assert((not has_right_shift<std::istream&, int>()()));
    assert((has_right_shift<std::istream&, int&>()()));
    assert((not has_right_shift<std::istream&, int const&>()()));
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
3

This question has already been answered several times, but there is a simpler way to check for the existence of operator== or basically any other operation (e.g., testing for a member function with a certain name), by using decltype together with the , operator:

namespace detail
{
    template<typename L, typename R>
    struct has_operator_equals_impl
    {
        template<typename T = L, typename U = R> // template parameters here to enable SFINAE
        static auto test(T &&t, U &&u) -> decltype(t == u, void(), std::true_type{});
        static auto test(...) -> std::false_type;
        using type = decltype(test(std::declval<L>(), std::declval<R>()));
    };
} // namespace detail

template<typename L, typename R = L>
struct has_operator_equals : detail::has_operator_equals_impl<L, R>::type {};

You can use this same approach to check if a type T has a member function foo which is invocable with a certain argument list:

namespace detail
{
    template<typename T, typename ...Args>
    struct has_member_foo_impl
    {
        template<typename T_ = T>
        static auto test(T_ &&t, Args &&...args) -> decltype(t.foo(std::forward<Args>(args)...), void(), std::true_type{});
        static auto test(...) -> std::false_type;
        using type = decltype(test(std::declval<T>(), std::declval<Args>()...));
    };
} // namespace detail

template<typename T, typename ...Args>
struct has_member_foo : detail::has_member_foo_impl<T, Args...>::type {};

I think this makes the intent of the code much clearer. In addition to that, this is a C++11 solution, so it doesn't depend on any newer C++14 or C++17 features. The end result is the same, of course, but this has become my preferred idiom for testing these kinds of things.

Edit: Fixed the insane case of the overloaded comma operator, I always miss that.

monkey0506
  • 2,489
  • 1
  • 21
  • 27
3

I know this question has long since been answered but I thought it might be worth noting for anyone who finds this question in the future that Boost just added a bunch of "has operator" traits to their type_traits library, and among them is has_equal_to, which does what OP was asking for.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
Cassie Dee
  • 3,145
  • 2
  • 18
  • 11
1

Lets consider a meta-function of the following form, which checks for the existence of equality operator (i.e ==) for the given type:

template<typename T>
struct equality { .... };

However, that might not be good enough for some corner cases. For example, say your class X does define operator== but it doesn't return bool, instead it returns Y. So in this case, what should equality<X>::value return? true or false? Well, that depends on the specific use case which we dont know now, and it doesn't seem to be a good idea to assume anything and force it on the users. However, in general we can assume that the return type should be bool, so lets express this in the interface itself:

template<typename T, typename R = bool>
struct equality { .... };

The default value for R is bool which indicates it is the general case. In cases, where the return type of operator== is different, say Y, then you can say this:

equality<X, Y>  //return type = Y

which checks for the given return-type as well. By default,

equality<X>   //return type = bool

Here is one implementation of this meta-function:

namespace details
{
    template <typename T, typename R, typename = R>
    struct equality : std::false_type {};

    template <typename T, typename R>
    struct equality<T,R,decltype(std::declval<T>()==std::declval<T>())> 
       : std::true_type {};
}

template<typename T, typename R = bool>
struct equality : details::equality<T, R> {};

Test:

struct A  {};
struct B  {  bool operator == (B const &); };
struct C  {  short operator == (C const &); };

int main()
{
    std::cout<< "equality<A>::value = " << equality<A>::value << std::endl;
    std::cout<< "equality<B>::value = " << equality<B>::value << std::endl;
    std::cout<< "equality<C>::value = " << equality<C>::value << std::endl;
    std::cout<< "equality<B,short>::value = " << equality<B,short>::value << std::endl;
    std::cout<< "equality<C,short>::value = " << equality<C,short>::value << std::endl;
}

Output:

equality<A>::value = 0
equality<B>::value = 1
equality<C>::value = 0
equality<B,short>::value = 0
equality<C,short>::value = 1

Online Demo

Hope that helps.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 1
    This is what Nicolas said, two months ago. – Lightness Races in Orbit Apr 01 '16 at 16:22
  • Not exactly. There is a difference. My solution checks for the *given* return type as well.. and thus it is flexible. – Nawaz Apr 01 '16 at 16:29
  • 2
    Ok, now I got what you were saying. You were probably referring to the various return types. Well, in that case I agree that my solution which was based on C++03 (nearly 5 years back), will not work if the type is not `bool` or something more conventional. Hence, I have reedited my answer. However, mostly return type of `operator==` is `bool` or at the max an integral type. Never an odd size like `bool[1313]`. But sometimes I have seen usage of function argument as non class type, e.g. `bool operator== (long)`, in such case none of the solutions will work: http://ideone.com/Yy3ByZ – iammilind Apr 01 '16 at 16:34
  • 2
    An `operator==` that returns anything other than `bool` is, frankly, not a true or useful `operator==`. I'd be quite happy for the trait to return `false` in that scenario. – Lightness Races in Orbit Apr 01 '16 at 16:39
  • `bool operator=(long)` is a good one. That can also be incorporated to my solution with little modification: http://coliru.stacked-crooked.com/a/2f9723892f9e1114 – Nawaz Apr 01 '16 at 16:45
  • 1
    @BarryTheHatchet: I cannot comment on the usefulness of return-type being different from `bool`, though I agree *in general* that is not a good idea. However, I dont remove the possibility of such a case where users want to return something else, possibly *contexually* convertible to bool. – Nawaz Apr 01 '16 at 16:48
  • Don't you feel that having an extra `template` parameter just to mention return type is an overkill. We can do it in much simpler way using C++11 by just making marginal modifications to my C++03 solution. See my answer above. Yes, regarding different argument for `operator==` we can have an extra template parameter, but not required otherwise. – iammilind Apr 01 '16 at 17:00
  • @iammilind: If you want to pass "return type", then you've to mention it in the interface, irrespective of the version of C++. As simple as that. – Nawaz Apr 01 '16 at 17:28
  • 1
    @Downvoter: Please specify the reason for the downvote. – Nawaz Apr 05 '16 at 14:56
1

c++17 slightly modified version of Richard Hodges godbolt

#include <functional>
#include <type_traits>

template<class T, class R, class ... Args>
std::is_convertible<std::invoke_result_t<T, Args...>, R> is_invokable_test(int);

template<class T, class R, class ... Args>
std::false_type is_invokable_test(...);

template<class T, class R, class ... Args>
using is_invokable = decltype(is_invokable_test<T, R, Args...>(0));

template<class T, class R, class ... Args>
constexpr auto is_invokable_v = is_invokable<T, R, Args...>::value;

template<class L, class R = L>
using has_equality = is_invokable<std::equal_to<>, bool, L, R>;
template<class L, class R = L>
constexpr auto has_equality_v = has_equality<L, R>::value;

struct L{};

int operator ==(int, L&&);

static_assert(has_equality_v<int>);
static_assert(!has_equality_v<L>);
static_assert(!has_equality_v<L, int>);
static_assert(has_equality_v<int, L>);
Anton Dyachenko
  • 169
  • 1
  • 9
1

In addition to @coder3101 answer, concepts can help you implement any function existence tests you want to. For example, std::equality_comparable is implemented using 4 simple tests, that check the following scenarios:

For A and B variables, make sure that the following expressions are valid:

A == B, returns bool
A != B, returns bool
B == A, returns bool
B != A, returns bool

If any one of them is illegal at compile time, the program won't compile. The implementation of this test (simplified from the standard):

template <typename T> concept equality_comparable
    = requires(T t, T u) {
        { t == u } -> std::convertible_to<bool>;
        { t != u } -> std::convertible_to<bool>;
        { u == t } -> std::convertible_to<bool>;
        { u != t } -> std::convertible_to<bool>;
    };

As you can see, you can customize this concept and create your own concept the fulfill your conditions. For example, if you want to force only the existence of operator==, you can do something like this:

template <typename T> concept my_equality_comparable
    = requires(T t, T u) {
        { t == u } -> std::convertible_to<bool>;
        { u == t } -> std::convertible_to<bool>;
    };

Read more about concepts in C++20.

Coral Kashri
  • 3,436
  • 2
  • 10
  • 22
1

We can use std::equal_to<Type> (or any other overloaded struct members) to make a more generic solution if we want to test binary operators (or other binary functors).

struct No {};

template<class T, class BinaryOperator>
struct ExistsBinaryOperator>
{
    enum { value = !std::is_same<decltype(std::declval<BinaryOperator>()(std::declval<T>(), std::declval<T>())), No>::value };
};

Usage:

using Type = int;
constexpr bool hasEqual = ExistsBinaryOperator<Type, std::equal_to<Type>>::value;
Marc Dirven
  • 309
  • 2
  • 18
  • The one big glitch, though, is that it's _more general for operators that accept two parameters_. If I want to test any other function with no arguments, one argument, or three or more arguments, your implementation doesn't work... – Alexis Wilke Sep 26 '21 at 22:18
1

This should work on C++11

template <class Void, template<class...> class Type, class... Args>
struct validator
{
     using value_t = std::false_type;
};

template <template<class...> class Type, class... Args>
struct validator< std::void_t<Type<Args...>>, Type, Args... >
{
     using value_t = std::true_type;
};
 
template <template<class...> class Type, class... Args>
using is_valid = typename validator<void, Type, Args...>::value_t;

template<typename... T>
using has_equal_t = decltype((std::declval<T&>().operator ==(std::declval<T&>()), ...));

template<typename... T>
using has_gequal_t = decltype((operator ==(std::declval<T&>(),std::declval<T&>()), ...));


struct EQ
{
     bool operator==(const EQ&) const;
};

struct GEQ
{
};
bool operator==(const GEQ&, const GEQ&);

struct NOEQ
{
};

static_assert(is_valid<has_equal_t,EQ>::value || is_valid<has_gequal_t,EQ>::value, "should have equal operator");
static_assert(is_valid<has_equal_t,GEQ>::value || is_valid<has_gequal_t,GEQ>::value, "should have equal operator");
// static_assert(is_valid<has_equal_t,NOEQ>::value || is_valid<has_gequal_t,NOEQ>::value, "should have equal operator"); // ERROR:
monneym
  • 11
  • 2
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 14 '23 at 06:29
0

IMO, this must be part of the class itself as it's deals with the private attributes of the class. The templates are interpreted at compile time. By default it generates operator==,constructor, destructor and copy constructor which do bit-wise copy (shallow copy) or bit-wise comparisons for the object of same type. The special cases (different types) must be overloaded. If you use global operator function you will have to declare the function as friend to access the private part or else you've to expose the interfaces required. Sometimes this is really ugly which may cause an unnecessary expose of a function.

VolAnd
  • 6,367
  • 3
  • 25
  • 43
sarat
  • 10,512
  • 7
  • 43
  • 74
  • This is true, but usually in big projects, you can not control what your colleagues are doing. – BЈовић Jul 01 '11 at 07:32
  • Not really. We started comparing objects of POD types, which were created long time ago, and most do not have operator==, and for those we use memcmp, but there are problems with padding. – BЈовић Jul 01 '11 at 11:16
0

Just for a reference, I am posting how I solved my problem, without a need to check if the operator== exists :

#include <iostream>
#include <cstring>

struct A
{
    int  a;
    char b;

    #if 0
    bool operator==( const A& r ) const
    {
        std::cout<<"calling member function"<<std::endl;

        return ( ( a==r.a ) && ( b==r.b ) );
    }
    #endif
};
#if 1
bool operator==( const A &l,const A &r )
{
    std::cout<<"calling NON-member function"<<std::endl;
    return ( ( l.a==r.a ) &&( l.b==r.b ) );
}
#endif

namespace details
{
struct anyType
{
    template < class S >
    anyType( const S &s ) :
        p(&s),
        sz(sizeof(s))
    {
    }

    const void *p;
    int sz;
};
bool operator==( const anyType &l, const anyType &r )
{
    std::cout<<"anyType::operator=="<<std::endl;
    return ( 0 == std::memcmp( l.p, r.p, l.sz ) );
}
} // namespace details

int main()
{
    A a1;
    a1.a=3;a1.b=0x12;
    A a2;
    a2.a=3;a2.b=0x12;

    using details::operator==;

    std::cout<< std::boolalpha << "numbers are equals : " << ( a1 == a2 ) <<std::endl;
}
BЈовић
  • 62,405
  • 41
  • 173
  • 273